Merge "Set vtable in class object to null after linking."
diff --git a/Android.mk b/Android.mk
index 7a95dfe..5a819f6 100644
--- a/Android.mk
+++ b/Android.mk
@@ -329,7 +329,7 @@
--dex-location=/$(1) --oat-file=$$@ \
--instruction-set=$(DEX2OAT_TARGET_ARCH) \
--instruction-set-features=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
- --android-root=$(PRODUCT_OUT)/system
+ --android-root=$(PRODUCT_OUT)/system --include-patch-information
endif
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index c39bc5d..353fab1 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -207,6 +207,25 @@
$(error LIBART_IMG_TARGET_BASE_ADDRESS unset)
endif
ART_TARGET_CFLAGS := $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS)
+
+ifndef LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA
+ LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA=-0x1000000
+endif
+ifndef LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA
+ LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA=0x1000000
+endif
+ART_HOST_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA)
+ART_HOST_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA)
+
+ifndef LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA
+ LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA=-0x1000000
+endif
+ifndef LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA
+ LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA=0x1000000
+endif
+ART_TARGET_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA)
+ART_TARGET_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA)
+
ART_TARGET_LDFLAGS :=
ifeq ($(TARGET_CPU_SMP),true)
ART_TARGET_CFLAGS += -DANDROID_SMP=1
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index d5cfc52..4987567 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -21,10 +21,7 @@
# List of known broken tests that we won't attempt to execute. The test name must be the full
# rule name such as test-art-host-oat-optimizing-HelloWorld64.
-ART_TEST_KNOWN_BROKEN := \
- test-art-host-oat-optimizing-SignalTest64 \
- test-art-host-oat-optimizing-SignalTest32 \
- test-art-target-oat-optimizing-SignalTest32
+ART_TEST_KNOWN_BROKEN :=
# List of known failing tests that when executed won't cause test execution to not finish.
# The test name must be the full rule name such as test-art-host-oat-optimizing-HelloWorld64.
@@ -48,6 +45,21 @@
# Do you want tests with the GC stress mode enabled run?
ART_TEST_GC_STRESS ?= $(ART_TEST_FULL)
+# Do you want oat tests with relocation enabled?
+ART_TEST_OAT_RELOCATE ?= true
+
+# Do you want oat tests with relocation disabled?
+ART_TEST_OAT_NO_RELOCATE ?= $(ART_TEST_FULL)
+
+# Do you want run-tests with relocation enabled?
+ART_TEST_RUN_TEST_RELOCATE ?= $(ART_TEST_FULL)
+
+# Do you want run-tests with relocation disabled?
+ART_TEST_RUN_TEST_NO_RELOCATE ?= $(ART_TEST_FULL)
+
+# Do you want run-tests with prebuild enabled?
+ART_TEST_RUN_TEST_PREBUILD ?= true
+
# Define the command run on test failure. $(1) is the name of the test. Executed by the shell.
define ART_TEST_FAILED
( [ -f $(ART_HOST_TEST_DIR)/skipped/$(1) ] || \
@@ -80,7 +92,7 @@
&& (echo -e "\e[93mSKIPPED TESTS\e[0m" && ls -1 $(ART_HOST_TEST_DIR)/skipped/) \
|| (echo -e "\e[92mNO TESTS SKIPPED\e[0m")) && \
([ -d $(ART_HOST_TEST_DIR)/failed/ ] \
- && (echo -e "\e[91mFAILING TESTS\e[0m" && ls -1 $(ART_HOST_TEST_DIR)/failed/) \
+ && (echo -e "\e[91mFAILING TESTS\e[0m" >&2 && ls -1 $(ART_HOST_TEST_DIR)/failed/ >&2) \
|| (echo -e "\e[92mNO TESTS FAILED\e[0m")) \
&& ([ ! -d $(ART_HOST_TEST_DIR)/failed/ ] && rm -r $(ART_HOST_TEST_DIR) \
|| (rm -r $(ART_HOST_TEST_DIR) && false)))))
diff --git a/compiler/Android.mk b/compiler/Android.mk
index ac2f9d6..61ff109 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -118,6 +118,7 @@
compilers.cc \
compiler.cc \
elf_fixup.cc \
+ elf_patcher.cc \
elf_stripper.cc \
elf_writer.cc \
elf_writer_quick.cc \
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index 6b96e92..5059c5f 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -358,7 +358,7 @@
*/
bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !IsLargeFrame(frame_size_, kArm);
NewLIR0(kPseudoMethodEntry);
- constexpr size_t kStackOverflowReservedUsableBytes = kArmStackOverflowReservedBytes -
+ const size_t kStackOverflowReservedUsableBytes = GetStackOverflowReservedBytes(kArm) -
Thread::kStackOverflowSignalReservedBytes;
bool large_frame = (static_cast<size_t>(frame_size_) > kStackOverflowReservedUsableBytes);
if (!skip_overflow_check) {
@@ -381,7 +381,7 @@
// This is done before the callee save instructions to avoid any possibility
// of these overflowing. This uses r12 and that's never saved in a callee
// save.
- OpRegRegImm(kOpSub, rs_r12, rs_rARM_SP, kArmStackOverflowReservedBytes);
+ OpRegRegImm(kOpSub, rs_r12, rs_rARM_SP, GetStackOverflowReservedBytes(kArm));
Load32Disp(rs_r12, 0, rs_r12);
MarkPossibleStackOverflowException();
}
diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h
index 8f60390..fa252a1 100644
--- a/compiler/dex/quick/arm/codegen_arm.h
+++ b/compiler/dex/quick/arm/codegen_arm.h
@@ -110,6 +110,8 @@
void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_src2);
void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src);
+ bool GenInlinedAbsFloat(CallInfo* info) OVERRIDE;
+ bool GenInlinedAbsDouble(CallInfo* info) OVERRIDE;
bool GenInlinedCas(CallInfo* info, bool is_long, bool is_object);
bool GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long);
bool GenInlinedSqrt(CallInfo* info);
diff --git a/compiler/dex/quick/arm/fp_arm.cc b/compiler/dex/quick/arm/fp_arm.cc
index e06d814..dcb8857 100644
--- a/compiler/dex/quick/arm/fp_arm.cc
+++ b/compiler/dex/quick/arm/fp_arm.cc
@@ -338,6 +338,60 @@
StoreValueWide(rl_dest, rl_result);
}
+static RegisterClass RegClassForAbsFP(RegLocation rl_src, RegLocation rl_dest) {
+ // If src is in a core reg or, unlikely, dest has been promoted to a core reg, use core reg.
+ if ((rl_src.location == kLocPhysReg && !rl_src.reg.IsFloat()) ||
+ (rl_dest.location == kLocPhysReg && !rl_dest.reg.IsFloat())) {
+ return kCoreReg;
+ }
+ // If src is in an fp reg or dest has been promoted to an fp reg, use fp reg.
+ if (rl_src.location == kLocPhysReg || rl_dest.location == kLocPhysReg) {
+ return kFPReg;
+ }
+ // With both src and dest in the stack frame we have to perform load+abs+store. Whether this
+ // is faster using a core reg or fp reg depends on the particular CPU. Without further
+ // investigation and testing we prefer core register. (If the result is subsequently used in
+ // another fp operation, the dalvik reg will probably get promoted and that should be handled
+ // by the cases above.)
+ return kCoreReg;
+}
+
+bool ArmMir2Lir::GenInlinedAbsFloat(CallInfo* info) {
+ if (info->result.location == kLocInvalid) {
+ return true; // Result is unused: inlining successful, no code generated.
+ }
+ RegLocation rl_dest = info->result;
+ RegLocation rl_src = UpdateLoc(info->args[0]);
+ RegisterClass reg_class = RegClassForAbsFP(rl_src, rl_dest);
+ rl_src = LoadValue(rl_src, reg_class);
+ RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
+ if (reg_class == kFPReg) {
+ NewLIR2(kThumb2Vabss, rl_result.reg.GetReg(), rl_src.reg.GetReg());
+ } else {
+ OpRegRegImm(kOpAnd, rl_result.reg, rl_src.reg, 0x7fffffff);
+ }
+ StoreValue(rl_dest, rl_result);
+ return true;
+}
+
+bool ArmMir2Lir::GenInlinedAbsDouble(CallInfo* info) {
+ if (info->result.location == kLocInvalid) {
+ return true; // Result is unused: inlining successful, no code generated.
+ }
+ RegLocation rl_dest = info->result;
+ RegLocation rl_src = UpdateLocWide(info->args[0]);
+ RegisterClass reg_class = RegClassForAbsFP(rl_src, rl_dest);
+ rl_src = LoadValueWide(rl_src, reg_class);
+ RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
+ if (reg_class == kFPReg) {
+ NewLIR2(kThumb2Vabsd, rl_result.reg.GetReg(), rl_src.reg.GetReg());
+ } else {
+ OpRegImm(kOpAnd, rl_result.reg.GetHigh(), 0x7fffffff);
+ }
+ StoreValueWide(rl_dest, rl_result);
+ return true;
+}
+
bool ArmMir2Lir::GenInlinedSqrt(CallInfo* info) {
DCHECK_EQ(cu_->instruction_set, kThumb2);
RegLocation rl_src = info->args[0];
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index d946ee3..5e95500 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -330,8 +330,8 @@
NewLIR0(kPseudoMethodEntry);
- constexpr size_t kStackOverflowReservedUsableBytes = kArm64StackOverflowReservedBytes -
- Thread::kStackOverflowSignalReservedBytes;
+ const size_t kStackOverflowReservedUsableBytes = GetStackOverflowReservedBytes(kArm64) -
+ Thread::kStackOverflowSignalReservedBytes;
const bool large_frame = static_cast<size_t>(frame_size_) > kStackOverflowReservedUsableBytes;
const int spill_count = num_core_spills_ + num_fp_spills_;
const int spill_size = (spill_count * kArm64PointerSize + 15) & ~0xf; // SP 16 byte alignment.
diff --git a/compiler/dex/quick/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h
index 4551e2a..8d15326 100644
--- a/compiler/dex/quick/arm64/codegen_arm64.h
+++ b/compiler/dex/quick/arm64/codegen_arm64.h
@@ -175,6 +175,7 @@
RegLocation rl_src2);
void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src);
bool GenInlinedReverseBits(CallInfo* info, OpSize size);
+ bool GenInlinedAbsFloat(CallInfo* info) OVERRIDE;
bool GenInlinedAbsDouble(CallInfo* info) OVERRIDE;
bool GenInlinedCas(CallInfo* info, bool is_long, bool is_object);
bool GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long);
diff --git a/compiler/dex/quick/arm64/fp_arm64.cc b/compiler/dex/quick/arm64/fp_arm64.cc
index 6594c4b..175cef1 100644
--- a/compiler/dex/quick/arm64/fp_arm64.cc
+++ b/compiler/dex/quick/arm64/fp_arm64.cc
@@ -323,12 +323,57 @@
StoreValueWide(rl_dest, rl_result);
}
+static RegisterClass RegClassForAbsFP(RegLocation rl_src, RegLocation rl_dest) {
+ // If src is in a core reg or, unlikely, dest has been promoted to a core reg, use core reg.
+ if ((rl_src.location == kLocPhysReg && !rl_src.reg.IsFloat()) ||
+ (rl_dest.location == kLocPhysReg && !rl_dest.reg.IsFloat())) {
+ return kCoreReg;
+ }
+ // If src is in an fp reg or dest has been promoted to an fp reg, use fp reg.
+ if (rl_src.location == kLocPhysReg || rl_dest.location == kLocPhysReg) {
+ return kFPReg;
+ }
+ // With both src and dest in the stack frame we have to perform load+abs+store. Whether this
+ // is faster using a core reg or fp reg depends on the particular CPU. For example, on A53
+ // it's faster using core reg while on A57 it's faster with fp reg, the difference being
+ // bigger on the A53. Without further investigation and testing we prefer core register.
+ // (If the result is subsequently used in another fp operation, the dalvik reg will probably
+ // get promoted and that should be handled by the cases above.)
+ return kCoreReg;
+}
+
+bool Arm64Mir2Lir::GenInlinedAbsFloat(CallInfo* info) {
+ if (info->result.location == kLocInvalid) {
+ return true; // Result is unused: inlining successful, no code generated.
+ }
+ RegLocation rl_dest = info->result;
+ RegLocation rl_src = UpdateLoc(info->args[0]);
+ RegisterClass reg_class = RegClassForAbsFP(rl_src, rl_dest);
+ rl_src = LoadValue(rl_src, reg_class);
+ RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
+ if (reg_class == kFPReg) {
+ NewLIR2(kA64Fabs2ff, rl_result.reg.GetReg(), rl_src.reg.GetReg());
+ } else {
+ NewLIR4(kA64Ubfm4rrdd, rl_result.reg.GetReg(), rl_src.reg.GetReg(), 0, 30);
+ }
+ StoreValue(rl_dest, rl_result);
+ return true;
+}
+
bool Arm64Mir2Lir::GenInlinedAbsDouble(CallInfo* info) {
- RegLocation rl_src = info->args[0];
- rl_src = LoadValueWide(rl_src, kCoreReg);
- RegLocation rl_dest = InlineTargetWide(info);
- RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
- NewLIR4(WIDE(kA64Ubfm4rrdd), rl_result.reg.GetReg(), rl_src.reg.GetReg(), 0, 62);
+ if (info->result.location == kLocInvalid) {
+ return true; // Result is unused: inlining successful, no code generated.
+ }
+ RegLocation rl_dest = info->result;
+ RegLocation rl_src = UpdateLocWide(info->args[0]);
+ RegisterClass reg_class = RegClassForAbsFP(rl_src, rl_dest);
+ rl_src = LoadValueWide(rl_src, reg_class);
+ RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
+ if (reg_class == kFPReg) {
+ NewLIR2(FWIDE(kA64Fabs2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
+ } else {
+ NewLIR4(WIDE(kA64Ubfm4rrdd), rl_result.reg.GetReg(), rl_src.reg.GetReg(), 0, 62);
+ }
StoreValueWide(rl_dest, rl_result);
return true;
}
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index fdb3d83..2c69593 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -1553,20 +1553,6 @@
return true;
}
-bool Mir2Lir::GenInlinedAbsFloat(CallInfo* info) {
- if (cu_->instruction_set == kMips) {
- // TODO - add Mips implementation
- return false;
- }
- RegLocation rl_src = info->args[0];
- rl_src = LoadValue(rl_src, kCoreReg);
- RegLocation rl_dest = InlineTarget(info);
- RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
- OpRegRegImm(kOpAnd, rl_result.reg, rl_src.reg, 0x7fffffff);
- StoreValue(rl_dest, rl_result);
- return true;
-}
-
bool Mir2Lir::GenInlinedReverseBits(CallInfo* info, OpSize size) {
// Currently implemented only for ARM64
return false;
@@ -1577,22 +1563,6 @@
return false;
}
-bool Mir2Lir::GenInlinedAbsDouble(CallInfo* info) {
- if (cu_->instruction_set == kMips) {
- // TODO - add Mips implementation
- return false;
- }
- RegLocation rl_src = info->args[0];
- rl_src = LoadValueWide(rl_src, kCoreReg);
- RegLocation rl_dest = InlineTargetWide(info);
- RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
-
- OpRegCopyWide(rl_result.reg, rl_src.reg);
- OpRegImm(kOpAnd, rl_result.reg.GetHigh(), 0x7fffffff);
- StoreValueWide(rl_dest, rl_result);
- return true;
-}
-
bool Mir2Lir::GenInlinedFloatCvt(CallInfo* info) {
if (cu_->instruction_set == kMips) {
// TODO - add Mips implementation
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
index 6a186b1..bb18ad2 100644
--- a/compiler/dex/quick/mips/codegen_mips.h
+++ b/compiler/dex/quick/mips/codegen_mips.h
@@ -110,6 +110,8 @@
void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_src2);
void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src);
+ bool GenInlinedAbsFloat(CallInfo* info) OVERRIDE;
+ bool GenInlinedAbsDouble(CallInfo* info) OVERRIDE;
bool GenInlinedCas(CallInfo* info, bool is_long, bool is_object);
bool GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long);
bool GenInlinedSqrt(CallInfo* info);
diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc
index 1f8f9ec..054514e 100644
--- a/compiler/dex/quick/mips/int_mips.cc
+++ b/compiler/dex/quick/mips/int_mips.cc
@@ -291,6 +291,16 @@
return false;
}
+bool MipsMir2Lir::GenInlinedAbsFloat(CallInfo* info) {
+ // TODO - add Mips implementation
+ return false;
+}
+
+bool MipsMir2Lir::GenInlinedAbsDouble(CallInfo* info) {
+ // TODO - add Mips implementation
+ return false;
+}
+
bool MipsMir2Lir::GenInlinedSqrt(CallInfo* info) {
DCHECK_NE(cu_->instruction_set, kThumb2);
return false;
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 512849f..6f4a965 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -32,6 +32,7 @@
#include "utils/array_ref.h"
#include "utils/arena_allocator.h"
#include "utils/growable_array.h"
+#include "utils/stack_checks.h"
namespace art {
@@ -205,36 +206,6 @@
#define SLOW_TYPE_PATH (cu_->enable_debug & (1 << kDebugSlowTypePath))
#define EXERCISE_SLOWEST_STRING_PATH (cu_->enable_debug & (1 << kDebugSlowestStringPath))
-// Size of a frame that we definitely consider large. Anything larger than this should
-// definitely get a stack overflow check.
-static constexpr size_t kLargeFrameSize = 2 * KB;
-
-// Size of a frame that should be small. Anything leaf method smaller than this should run
-// without a stack overflow check.
-// The constant is from experience with frameworks code.
-static constexpr size_t kSmallFrameSize = 1 * KB;
-
-// Determine whether a frame is small or large, used in the decision on whether to elide a
-// stack overflow check on method entry.
-//
-// A frame is considered large when it's either above kLargeFrameSize, or a quarter of the
-// overflow-usable stack space.
-static constexpr bool IsLargeFrame(size_t size, InstructionSet isa) {
- return size >= kLargeFrameSize || size >= GetStackOverflowReservedBytes(isa) / 4;
-}
-
-// We want to ensure that on all systems kSmallFrameSize will lead to false in IsLargeFrame.
-COMPILE_ASSERT(!IsLargeFrame(kSmallFrameSize, kArm),
- kSmallFrameSize_is_not_a_small_frame_arm);
-COMPILE_ASSERT(!IsLargeFrame(kSmallFrameSize, kArm64),
- kSmallFrameSize_is_not_a_small_frame_arm64);
-COMPILE_ASSERT(!IsLargeFrame(kSmallFrameSize, kMips),
- kSmallFrameSize_is_not_a_small_frame_mips);
-COMPILE_ASSERT(!IsLargeFrame(kSmallFrameSize, kX86),
- kSmallFrameSize_is_not_a_small_frame_x86);
-COMPILE_ASSERT(!IsLargeFrame(kSmallFrameSize, kX86_64),
- kSmallFrameSize_is_not_a_small_frame_x64_64);
-
class Mir2Lir : public Backend {
public:
static constexpr bool kFailOnSizeError = true && kIsDebugBuild;
@@ -990,8 +961,8 @@
bool GenInlinedReverseBytes(CallInfo* info, OpSize size);
bool GenInlinedAbsInt(CallInfo* info);
virtual bool GenInlinedAbsLong(CallInfo* info);
- virtual bool GenInlinedAbsFloat(CallInfo* info);
- virtual bool GenInlinedAbsDouble(CallInfo* info);
+ virtual bool GenInlinedAbsFloat(CallInfo* info) = 0;
+ virtual bool GenInlinedAbsDouble(CallInfo* info) = 0;
bool GenInlinedFloatCvt(CallInfo* info);
bool GenInlinedDoubleCvt(CallInfo* info);
virtual bool GenInlinedArrayCopyCharArray(CallInfo* info);
diff --git a/compiler/dex/quick_compiler_callbacks.h b/compiler/dex/quick_compiler_callbacks.h
index 7c9614f..cdf71b6 100644
--- a/compiler/dex/quick_compiler_callbacks.h
+++ b/compiler/dex/quick_compiler_callbacks.h
@@ -41,6 +41,11 @@
void ClassRejected(ClassReference ref) OVERRIDE;
+ // We are running in an environment where we can call patchoat safely so we should.
+ bool IsRelocationPossible() OVERRIDE {
+ return true;
+ }
+
private:
VerificationResults* const verification_results_;
DexFileToMethodInlinerMap* const method_inliner_map_;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 9e88c8d..f85bc65 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -955,7 +955,8 @@
if (class_in_image) {
// boot -> app class pointers.
*is_type_initialized = resolved_class->IsInitialized();
- *use_direct_type_ptr = true;
+ // TODO This is somewhat hacky. We should refactor all of this invoke codepath.
+ *use_direct_type_ptr = !GetCompilerOptions().GetIncludePatchInformation();
*direct_type_ptr = reinterpret_cast<uintptr_t>(resolved_class);
return true;
} else {
@@ -1099,6 +1100,9 @@
*direct_method = 0;
bool use_dex_cache = false;
const bool compiling_boot = Runtime::Current()->GetHeap()->IsCompilingBoot();
+ // TODO This is somewhat hacky. We should refactor all of this invoke codepath.
+ const bool force_relocations = (compiling_boot ||
+ GetCompilerOptions().GetIncludePatchInformation());
if (compiler_->IsPortable()) {
if (sharp_type != kStatic && sharp_type != kDirect) {
return;
@@ -1109,7 +1113,7 @@
return;
}
// TODO: support patching on all architectures.
- use_dex_cache = compiling_boot && !support_boot_image_fixup_;
+ use_dex_cache = force_relocations && !support_boot_image_fixup_;
}
bool method_code_in_boot = (method->GetDeclaringClass()->GetClassLoader() == nullptr);
if (!use_dex_cache) {
@@ -1128,8 +1132,8 @@
if (method_code_in_boot) {
*stats_flags |= kFlagDirectCallToBoot | kFlagDirectMethodToBoot;
}
- if (!use_dex_cache && compiling_boot) {
- if (!IsImageClass(method->GetDeclaringClassDescriptor())) {
+ if (!use_dex_cache && force_relocations) {
+ if (!IsImage() || !IsImageClass(method->GetDeclaringClassDescriptor())) {
// We can only branch directly to Methods that are resolved in the DexCache.
// Otherwise we won't invoke the resolution trampoline.
use_dex_cache = true;
@@ -1150,7 +1154,7 @@
if (dex_method_idx != DexFile::kDexNoIndex) {
target_method->dex_method_index = dex_method_idx;
} else {
- if (compiling_boot && !use_dex_cache) {
+ if (force_relocations && !use_dex_cache) {
target_method->dex_method_index = method->GetDexMethodIndex();
target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
}
@@ -1167,19 +1171,26 @@
*type = sharp_type;
}
} else {
- bool method_in_image = compiling_boot ||
+ bool method_in_image =
Runtime::Current()->GetHeap()->FindSpaceFromObject(method, false)->IsImageSpace();
- if (method_in_image) {
+ if (method_in_image || compiling_boot) {
+ // We know we must be able to get to the method in the image, so use that pointer.
CHECK(!method->IsAbstract());
*type = sharp_type;
- *direct_method = compiling_boot ? -1 : reinterpret_cast<uintptr_t>(method);
- *direct_code = compiling_boot ? -1 : compiler_->GetEntryPointOf(method);
+ *direct_method = force_relocations ? -1 : reinterpret_cast<uintptr_t>(method);
+ *direct_code = force_relocations ? -1 : compiler_->GetEntryPointOf(method);
target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
target_method->dex_method_index = method->GetDexMethodIndex();
} else if (!must_use_direct_pointers) {
// Set the code and rely on the dex cache for the method.
*type = sharp_type;
- *direct_code = compiler_->GetEntryPointOf(method);
+ if (force_relocations) {
+ *direct_code = -1;
+ target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
+ target_method->dex_method_index = method->GetDexMethodIndex();
+ } else {
+ *direct_code = compiler_->GetEntryPointOf(method);
+ }
} else {
// Direct pointers were required but none were available.
VLOG(compiler) << "Dex cache devirtualization failed for: " << PrettyMethod(method);
diff --git a/compiler/elf_patcher.cc b/compiler/elf_patcher.cc
new file mode 100644
index 0000000..6112fbb
--- /dev/null
+++ b/compiler/elf_patcher.cc
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2014 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 "elf_patcher.h"
+
+#include <vector>
+#include <set>
+
+#include "elf_file.h"
+#include "elf_utils.h"
+#include "mirror/art_field-inl.h"
+#include "mirror/art_method-inl.h"
+#include "mirror/array-inl.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
+#include "mirror/dex_cache-inl.h"
+#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
+#include "mirror/string-inl.h"
+#include "oat.h"
+#include "os.h"
+#include "utils.h"
+
+namespace art {
+
+bool ElfPatcher::Patch(const CompilerDriver* driver, ElfFile* elf_file,
+ const std::string& oat_location,
+ ImageAddressCallback cb, void* cb_data,
+ std::string* error_msg) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
+ if (oat_file == nullptr) {
+ CHECK(Runtime::Current()->IsCompiler());
+ oat_file = OatFile::Open(oat_location, oat_location, NULL, false, error_msg);
+ if (oat_file == nullptr) {
+ *error_msg = StringPrintf("Unable to find or open oat file at '%s': %s", oat_location.c_str(),
+ error_msg->c_str());
+ return false;
+ }
+ CHECK_EQ(class_linker->RegisterOatFile(oat_file), oat_file);
+ }
+ return ElfPatcher::Patch(driver, elf_file, oat_file,
+ reinterpret_cast<uintptr_t>(oat_file->Begin()), cb, cb_data, error_msg);
+}
+
+bool ElfPatcher::Patch(const CompilerDriver* driver, ElfFile* elf, const OatFile* oat_file,
+ uintptr_t oat_data_start, ImageAddressCallback cb, void* cb_data,
+ std::string* error_msg) {
+ Elf32_Shdr* data_sec = elf->FindSectionByName(".rodata");
+ if (data_sec == nullptr) {
+ *error_msg = "Unable to find .rodata section and oat header";
+ return false;
+ }
+ OatHeader* oat_header = reinterpret_cast<OatHeader*>(elf->Begin() + data_sec->sh_offset);
+ if (!oat_header->IsValid()) {
+ *error_msg = "Oat header was not valid";
+ return false;
+ }
+
+ ElfPatcher p(driver, elf, oat_file, oat_header, oat_data_start, cb, cb_data, error_msg);
+ return p.PatchElf();
+}
+
+mirror::ArtMethod* ElfPatcher::GetTargetMethod(const CompilerDriver::CallPatchInformation* patch) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ StackHandleScope<1> hs(Thread::Current());
+ Handle<mirror::DexCache> dex_cache(
+ hs.NewHandle(class_linker->FindDexCache(*patch->GetTargetDexFile())));
+ mirror::ArtMethod* method = class_linker->ResolveMethod(*patch->GetTargetDexFile(),
+ patch->GetTargetMethodIdx(),
+ dex_cache,
+ NullHandle<mirror::ClassLoader>(),
+ NullHandle<mirror::ArtMethod>(),
+ patch->GetTargetInvokeType());
+ CHECK(method != NULL)
+ << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetTargetMethodIdx();
+ CHECK(!method->IsRuntimeMethod())
+ << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetTargetMethodIdx();
+ CHECK(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx()) == method)
+ << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetReferrerMethodIdx() << " "
+ << PrettyMethod(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx())) << " "
+ << PrettyMethod(method);
+ return method;
+}
+
+mirror::Class* ElfPatcher::GetTargetType(const CompilerDriver::TypePatchInformation* patch) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ StackHandleScope<2> hs(Thread::Current());
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(patch->GetDexFile())));
+ mirror::Class* klass = class_linker->ResolveType(patch->GetDexFile(), patch->GetTargetTypeIdx(),
+ dex_cache, NullHandle<mirror::ClassLoader>());
+ CHECK(klass != NULL)
+ << patch->GetDexFile().GetLocation() << " " << patch->GetTargetTypeIdx();
+ CHECK(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx()) == klass)
+ << patch->GetDexFile().GetLocation() << " " << patch->GetReferrerMethodIdx() << " "
+ << PrettyClass(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx())) << " "
+ << PrettyClass(klass);
+ return klass;
+}
+
+void ElfPatcher::AddPatch(uintptr_t p) {
+ if (write_patches_ && patches_set_.find(p) == patches_set_.end()) {
+ patches_set_.insert(p);
+ patches_.push_back(p);
+ }
+}
+
+uint32_t* ElfPatcher::GetPatchLocation(uintptr_t patch_ptr) {
+ CHECK_GE(patch_ptr, reinterpret_cast<uintptr_t>(oat_file_->Begin()));
+ uintptr_t off = patch_ptr - reinterpret_cast<uintptr_t>(oat_file_->Begin());
+ uintptr_t ret = reinterpret_cast<uintptr_t>(oat_header_) + off;
+
+ CHECK_GE(ret, reinterpret_cast<uintptr_t>(elf_file_->Begin()));
+ CHECK_LT(ret, reinterpret_cast<uintptr_t>(elf_file_->End()));
+ return reinterpret_cast<uint32_t*>(ret);
+}
+
+void ElfPatcher::SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const void* quick_oat_code = class_linker->GetQuickOatCodeFor(patch->GetDexFile(),
+ patch->GetReferrerClassDefIdx(),
+ patch->GetReferrerMethodIdx());
+ // TODO: make this Thumb2 specific
+ uint8_t* base = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(quick_oat_code) & ~0x1);
+ uintptr_t patch_ptr = reinterpret_cast<uintptr_t>(base + patch->GetLiteralOffset());
+ uint32_t* patch_location = GetPatchLocation(patch_ptr);
+ if (kIsDebugBuild) {
+ if (patch->IsCall()) {
+ const CompilerDriver::CallPatchInformation* cpatch = patch->AsCall();
+ const DexFile::MethodId& id =
+ cpatch->GetTargetDexFile()->GetMethodId(cpatch->GetTargetMethodIdx());
+ uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
+ uint32_t actual = *patch_location;
+ CHECK(actual == expected || actual == value) << std::hex
+ << "actual=" << actual
+ << "expected=" << expected
+ << "value=" << value;
+ }
+ if (patch->IsType()) {
+ const CompilerDriver::TypePatchInformation* tpatch = patch->AsType();
+ const DexFile::TypeId& id = tpatch->GetDexFile().GetTypeId(tpatch->GetTargetTypeIdx());
+ uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
+ uint32_t actual = *patch_location;
+ CHECK(actual == expected || actual == value) << std::hex
+ << "actual=" << actual
+ << "expected=" << expected
+ << "value=" << value;
+ }
+ }
+ *patch_location = value;
+ oat_header_->UpdateChecksum(patch_location, sizeof(value));
+
+ if (patch->IsCall() && patch->AsCall()->IsRelative()) {
+ // We never record relative patches.
+ return;
+ }
+ uintptr_t loc = patch_ptr - (reinterpret_cast<uintptr_t>(oat_file_->Begin()) +
+ oat_header_->GetExecutableOffset());
+ CHECK_GT(patch_ptr, reinterpret_cast<uintptr_t>(oat_file_->Begin()) +
+ oat_header_->GetExecutableOffset());
+ CHECK_LT(loc, oat_file_->Size() - oat_header_->GetExecutableOffset());
+ AddPatch(loc);
+}
+
+bool ElfPatcher::PatchElf() {
+ // TODO if we are adding patches the resulting ELF file might have a
+ // potentially rather large amount of free space where patches might have been
+ // placed. We should adjust the ELF file to get rid of this excess space.
+ if (write_patches_) {
+ patches_.reserve(compiler_driver_->GetCodeToPatch().size() +
+ compiler_driver_->GetMethodsToPatch().size() +
+ compiler_driver_->GetClassesToPatch().size());
+ }
+ Thread* self = Thread::Current();
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const char* old_cause = self->StartAssertNoThreadSuspension("ElfPatcher");
+
+ typedef std::vector<const CompilerDriver::CallPatchInformation*> CallPatches;
+ const CallPatches& code_to_patch = compiler_driver_->GetCodeToPatch();
+ for (size_t i = 0; i < code_to_patch.size(); i++) {
+ const CompilerDriver::CallPatchInformation* patch = code_to_patch[i];
+
+ mirror::ArtMethod* target = GetTargetMethod(patch);
+ uintptr_t quick_code = reinterpret_cast<uintptr_t>(class_linker->GetQuickOatCodeFor(target));
+ DCHECK_NE(quick_code, 0U) << PrettyMethod(target);
+ const OatFile* target_oat = class_linker->FindOpenedOatFileForDexFile(*patch->GetTargetDexFile());
+ // Get where the data actually starts. if target is this oat_file_ it is oat_data_start_,
+ // otherwise it is wherever target_oat is loaded.
+ uintptr_t oat_data_addr = GetBaseAddressFor(target_oat);
+ uintptr_t code_base = reinterpret_cast<uintptr_t>(target_oat->Begin());
+ uintptr_t code_offset = quick_code - code_base;
+ bool is_quick_offset = false;
+ if (quick_code == reinterpret_cast<uintptr_t>(GetQuickToInterpreterBridge())) {
+ is_quick_offset = true;
+ code_offset = oat_header_->GetQuickToInterpreterBridgeOffset();
+ } else if (quick_code ==
+ reinterpret_cast<uintptr_t>(class_linker->GetQuickGenericJniTrampoline())) {
+ CHECK(target->IsNative());
+ is_quick_offset = true;
+ code_offset = oat_header_->GetQuickGenericJniTrampolineOffset();
+ }
+ uintptr_t value;
+ if (patch->IsRelative()) {
+ // value to patch is relative to the location being patched
+ const void* quick_oat_code =
+ class_linker->GetQuickOatCodeFor(patch->GetDexFile(),
+ patch->GetReferrerClassDefIdx(),
+ patch->GetReferrerMethodIdx());
+ if (is_quick_offset) {
+ // If its a quick offset it means that we are doing a relative patch from the class linker
+ // oat_file to the elf_patcher oat_file so we need to adjust the quick oat code to be the
+ // one in the output oat_file (ie where it is actually going to be loaded).
+ quick_code = PointerToLowMemUInt32(reinterpret_cast<void*>(oat_data_addr + code_offset));
+ quick_oat_code =
+ reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(quick_oat_code) +
+ oat_data_addr - code_base);
+ }
+ uintptr_t base = reinterpret_cast<uintptr_t>(quick_oat_code);
+ uintptr_t patch_location = base + patch->GetLiteralOffset();
+ value = quick_code - patch_location + patch->RelativeOffset();
+ } else if (code_offset != 0) {
+ value = PointerToLowMemUInt32(reinterpret_cast<void*>(oat_data_addr + code_offset));
+ } else {
+ value = 0;
+ }
+ SetPatchLocation(patch, value);
+ }
+
+ const CallPatches& methods_to_patch = compiler_driver_->GetMethodsToPatch();
+ for (size_t i = 0; i < methods_to_patch.size(); i++) {
+ const CompilerDriver::CallPatchInformation* patch = methods_to_patch[i];
+ mirror::ArtMethod* target = GetTargetMethod(patch);
+ SetPatchLocation(patch, PointerToLowMemUInt32(get_image_address_(cb_data_, target)));
+ }
+
+ const std::vector<const CompilerDriver::TypePatchInformation*>& classes_to_patch =
+ compiler_driver_->GetClassesToPatch();
+ for (size_t i = 0; i < classes_to_patch.size(); i++) {
+ const CompilerDriver::TypePatchInformation* patch = classes_to_patch[i];
+ mirror::Class* target = GetTargetType(patch);
+ SetPatchLocation(patch, PointerToLowMemUInt32(get_image_address_(cb_data_, target)));
+ }
+
+ self->EndAssertNoThreadSuspension(old_cause);
+
+ if (write_patches_) {
+ return WriteOutPatchData();
+ }
+ return true;
+}
+
+bool ElfPatcher::WriteOutPatchData() {
+ Elf32_Shdr* shdr = elf_file_->FindSectionByName(".oat_patches");
+ if (shdr != nullptr) {
+ CHECK_EQ(shdr, elf_file_->FindSectionByType(SHT_OAT_PATCH))
+ << "Incorrect type for .oat_patches section";
+ CHECK_LE(patches_.size() * sizeof(uintptr_t), shdr->sh_size)
+ << "We got more patches than anticipated";
+ CHECK_LE(reinterpret_cast<uintptr_t>(elf_file_->Begin()) + shdr->sh_offset + shdr->sh_size,
+ reinterpret_cast<uintptr_t>(elf_file_->End())) << "section is too large";
+ CHECK(shdr == &elf_file_->GetSectionHeader(elf_file_->GetSectionHeaderNum() - 1) ||
+ shdr->sh_offset + shdr->sh_size <= (shdr + 1)->sh_offset)
+ << "Section overlaps onto next section";
+ // It's mmap'd so we can just memcpy.
+ memcpy(elf_file_->Begin() + shdr->sh_offset, patches_.data(),
+ patches_.size() * sizeof(uintptr_t));
+ // TODO We should fill in the newly empty space between the last patch and
+ // the start of the next section by moving the following sections down if
+ // possible.
+ shdr->sh_size = patches_.size() * sizeof(uintptr_t);
+ return true;
+ } else {
+ LOG(ERROR) << "Unable to find section header for SHT_OAT_PATCH";
+ *error_msg_ = "Unable to find section to write patch information to in ";
+ *error_msg_ += elf_file_->GetFile().GetPath();
+ return false;
+ }
+}
+
+} // namespace art
diff --git a/compiler/elf_patcher.h b/compiler/elf_patcher.h
new file mode 100644
index 0000000..0a9f0a01
--- /dev/null
+++ b/compiler/elf_patcher.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2014 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_ELF_PATCHER_H_
+#define ART_COMPILER_ELF_PATCHER_H_
+
+#include "base/mutex.h"
+#include "driver/compiler_driver.h"
+#include "elf_file.h"
+#include "mirror/art_method.h"
+#include "mirror/class.h"
+#include "mirror/object.h"
+#include "oat_file.h"
+#include "oat.h"
+#include "os.h"
+
+namespace art {
+
+class ElfPatcher {
+ public:
+ typedef void* (*ImageAddressCallback)(void* data, mirror::Object* obj);
+
+ static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
+ const std::string& oat_location,
+ ImageAddressCallback cb, void* cb_data,
+ std::string* error_msg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
+ const OatFile* oat_file, uintptr_t oat_data_begin,
+ ImageAddressCallback cb, void* cb_data,
+ std::string* error_msg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
+ const std::string& oat_location,
+ std::string* error_msg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return ElfPatcher::Patch(driver, elf_file, oat_location,
+ DefaultImageAddressCallback, nullptr, error_msg);
+ }
+
+ static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
+ const OatFile* oat_file, uintptr_t oat_data_begin,
+ std::string* error_msg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return ElfPatcher::Patch(driver, elf_file, oat_file, oat_data_begin,
+ DefaultImageAddressCallback, nullptr, error_msg);
+ }
+
+ private:
+ ElfPatcher(const CompilerDriver* driver, ElfFile* elf_file, const OatFile* oat_file,
+ OatHeader* oat_header, uintptr_t oat_data_begin,
+ ImageAddressCallback cb, void* cb_data, std::string* error_msg)
+ : compiler_driver_(driver), elf_file_(elf_file), oat_file_(oat_file),
+ oat_header_(oat_header), oat_data_begin_(oat_data_begin), get_image_address_(cb),
+ cb_data_(cb_data), error_msg_(error_msg),
+ write_patches_(compiler_driver_->GetCompilerOptions().GetIncludePatchInformation()) {}
+ ~ElfPatcher() {}
+
+ static void* DefaultImageAddressCallback(void* data_unused, mirror::Object* obj) {
+ return static_cast<void*>(obj);
+ }
+
+ bool PatchElf()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ mirror::ArtMethod* GetTargetMethod(const CompilerDriver::CallPatchInformation* patch)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ mirror::Class* GetTargetType(const CompilerDriver::TypePatchInformation* patch)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void AddPatch(uintptr_t off);
+
+ void SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Takes the pointer into the oat_file_ and get the pointer in to the ElfFile.
+ uint32_t* GetPatchLocation(uintptr_t patch_ptr);
+
+ bool WriteOutPatchData();
+
+ uintptr_t GetBaseAddressFor(const OatFile* f) {
+ if (f == oat_file_) {
+ return oat_data_begin_;
+ } else {
+ return reinterpret_cast<uintptr_t>(f->Begin());
+ }
+ }
+
+ const CompilerDriver* compiler_driver_;
+
+ // The elf_file containing the oat_data we are patching up
+ ElfFile* elf_file_;
+
+ // The oat_file that is actually loaded.
+ const OatFile* oat_file_;
+
+ // The oat_header_ within the elf_file_
+ OatHeader* oat_header_;
+
+ // Where the elf_file will be loaded during normal runs.
+ uintptr_t oat_data_begin_;
+
+ // Callback to get image addresses.
+ ImageAddressCallback get_image_address_;
+ void* cb_data_;
+
+ std::string* error_msg_;
+ std::vector<uintptr_t> patches_;
+ std::set<uintptr_t> patches_set_;
+ bool write_patches_;
+
+ DISALLOW_COPY_AND_ASSIGN(ElfPatcher);
+};
+
+} // namespace art
+#endif // ART_COMPILER_ELF_PATCHER_H_
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index fe4fcd4..3005e56 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -80,7 +80,7 @@
t.NewTiming("WriteElf");
ScopedObjectAccess soa(Thread::Current());
SafeMap<std::string, std::string> key_value_store;
- OatWriter oat_writer(class_linker->GetBootClassPath(), 0, 0, compiler_driver_.get(), &timings,
+ OatWriter oat_writer(class_linker->GetBootClassPath(), 0, 0, 0, compiler_driver_.get(), &timings,
&key_value_store);
bool success = compiler_driver_->WriteElf(GetTestAndroidRoot(),
!kIsTargetBuild,
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 8ef2964..d102bbc 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -29,6 +29,7 @@
#include "driver/compiler_driver.h"
#include "elf_file.h"
#include "elf_utils.h"
+#include "elf_patcher.h"
#include "elf_writer.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap.h"
@@ -84,7 +85,7 @@
return false;
}
std::string error_msg;
- oat_file_ = OatFile::OpenWritable(oat_file.get(), oat_location, &error_msg);
+ oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_location, &error_msg);
if (oat_file_ == nullptr) {
LOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location
<< ": " << error_msg;
@@ -801,214 +802,35 @@
}
}
-static ArtMethod* GetTargetMethod(const CompilerDriver::CallPatchInformation* patch)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::DexCache> dex_cache(
- hs.NewHandle(class_linker->FindDexCache(*patch->GetTargetDexFile())));
- ArtMethod* method = class_linker->ResolveMethod(*patch->GetTargetDexFile(),
- patch->GetTargetMethodIdx(),
- dex_cache,
- NullHandle<mirror::ClassLoader>(),
- NullHandle<mirror::ArtMethod>(),
- patch->GetTargetInvokeType());
- CHECK(method != NULL)
- << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetTargetMethodIdx();
- CHECK(!method->IsRuntimeMethod())
- << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetTargetMethodIdx();
- CHECK(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx()) == method)
- << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetReferrerMethodIdx() << " "
- << PrettyMethod(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx())) << " "
- << PrettyMethod(method);
- return method;
-}
-
-static Class* GetTargetType(const CompilerDriver::TypePatchInformation* patch)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- StackHandleScope<2> hs(Thread::Current());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(patch->GetDexFile())));
- Class* klass = class_linker->ResolveType(patch->GetDexFile(), patch->GetTargetTypeIdx(),
- dex_cache, NullHandle<mirror::ClassLoader>());
- CHECK(klass != NULL)
- << patch->GetDexFile().GetLocation() << " " << patch->GetTargetTypeIdx();
- CHECK(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx()) == klass)
- << patch->GetDexFile().GetLocation() << " " << patch->GetReferrerMethodIdx() << " "
- << PrettyClass(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx())) << " "
- << PrettyClass(klass);
- return klass;
+static OatHeader* GetOatHeaderFromElf(ElfFile* elf) {
+ Elf32_Shdr* data_sec = elf->FindSectionByName(".rodata");
+ if (data_sec == nullptr) {
+ return nullptr;
+ }
+ return reinterpret_cast<OatHeader*>(elf->Begin() + data_sec->sh_offset);
}
void ImageWriter::PatchOatCodeAndMethods(File* elf_file) {
- std::vector<uintptr_t> patches;
- std::set<uintptr_t> patches_set;
- auto maybe_push = [&patches, &patches_set] (uintptr_t p) {
- if (patches_set.find(p) == patches_set.end()) {
- patches.push_back(p);
- patches_set.insert(p);
- }
- };
- const bool add_patches = compiler_driver_.GetCompilerOptions().GetIncludePatchInformation();
- if (add_patches) {
- // TODO if we are adding patches the resulting ELF file might have a potentially rather large
- // amount of free space where patches might have been placed. We should adjust the ELF file to
- // get rid of this excess space.
- patches.reserve(compiler_driver_.GetCodeToPatch().size() +
- compiler_driver_.GetMethodsToPatch().size() +
- compiler_driver_.GetClassesToPatch().size());
+ std::string error_msg;
+ std::unique_ptr<ElfFile> elf(ElfFile::Open(elf_file, PROT_READ|PROT_WRITE,
+ MAP_SHARED, &error_msg));
+ if (elf.get() == nullptr) {
+ LOG(FATAL) << "Unable patch oat file: " << error_msg;
+ return;
}
- uintptr_t loc = 0;
- Thread* self = Thread::Current();
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter");
-
- typedef std::vector<const CompilerDriver::CallPatchInformation*> CallPatches;
- const CallPatches& code_to_patch = compiler_driver_.GetCodeToPatch();
- for (size_t i = 0; i < code_to_patch.size(); i++) {
- const CompilerDriver::CallPatchInformation* patch = code_to_patch[i];
- ArtMethod* target = GetTargetMethod(patch);
- uintptr_t quick_code = reinterpret_cast<uintptr_t>(class_linker->GetQuickOatCodeFor(target));
- DCHECK_NE(quick_code, 0U) << PrettyMethod(target);
- uintptr_t code_base = reinterpret_cast<uintptr_t>(&oat_file_->GetOatHeader());
- uintptr_t code_offset = quick_code - code_base;
- bool is_quick_offset = false;
- if (quick_code == reinterpret_cast<uintptr_t>(GetQuickToInterpreterBridge())) {
- is_quick_offset = true;
- code_offset = quick_to_interpreter_bridge_offset_;
- } else if (quick_code ==
- reinterpret_cast<uintptr_t>(class_linker->GetQuickGenericJniTrampoline())) {
- CHECK(target->IsNative());
- is_quick_offset = true;
- code_offset = quick_generic_jni_trampoline_offset_;
- }
- uintptr_t value;
- if (patch->IsRelative()) {
- // value to patch is relative to the location being patched
- const void* quick_oat_code =
- class_linker->GetQuickOatCodeFor(patch->GetDexFile(),
- patch->GetReferrerClassDefIdx(),
- patch->GetReferrerMethodIdx());
- if (is_quick_offset) {
- // If its a quick offset it means that we are doing a relative patch from the class linker
- // oat_file to the image writer oat_file so we need to adjust the quick oat code to be the
- // one in the image writer oat_file.
- quick_code = PointerToLowMemUInt32(GetOatAddress(code_offset));
- quick_oat_code =
- reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(quick_oat_code) +
- reinterpret_cast<uintptr_t>(oat_data_begin_) - code_base);
- }
- uintptr_t base = reinterpret_cast<uintptr_t>(quick_oat_code);
- uintptr_t patch_location = base + patch->GetLiteralOffset();
- value = quick_code - patch_location + patch->RelativeOffset();
- } else {
- value = PointerToLowMemUInt32(GetOatAddress(code_offset));
- }
- SetPatchLocation(patch, value, &loc);
- if (add_patches && !patch->AsCall()->IsRelative()) {
- maybe_push(loc);
- }
+ if (!ElfPatcher::Patch(&compiler_driver_, elf.get(), oat_file_,
+ reinterpret_cast<uintptr_t>(oat_data_begin_),
+ GetImageAddressCallback, reinterpret_cast<void*>(this),
+ &error_msg)) {
+ LOG(FATAL) << "unable to patch oat file: " << error_msg;
+ return;
}
+ OatHeader* oat_header = GetOatHeaderFromElf(elf.get());
+ CHECK(oat_header != nullptr);
+ CHECK(oat_header->IsValid());
- const CallPatches& methods_to_patch = compiler_driver_.GetMethodsToPatch();
- for (size_t i = 0; i < methods_to_patch.size(); i++) {
- const CompilerDriver::CallPatchInformation* patch = methods_to_patch[i];
- ArtMethod* target = GetTargetMethod(patch);
- SetPatchLocation(patch, PointerToLowMemUInt32(GetImageAddress(target)), &loc);
- if (add_patches && !patch->AsCall()->IsRelative()) {
- maybe_push(loc);
- }
- }
-
- const std::vector<const CompilerDriver::TypePatchInformation*>& classes_to_patch =
- compiler_driver_.GetClassesToPatch();
- for (size_t i = 0; i < classes_to_patch.size(); i++) {
- const CompilerDriver::TypePatchInformation* patch = classes_to_patch[i];
- Class* target = GetTargetType(patch);
- SetPatchLocation(patch, PointerToLowMemUInt32(GetImageAddress(target)), &loc);
- if (add_patches) {
- maybe_push(loc);
- }
- }
-
- // Update the image header with the new checksum after patching
ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
- image_header->SetOatChecksum(oat_file_->GetOatHeader().GetChecksum());
- self->EndAssertNoThreadSuspension(old_cause);
-
- // Update the ElfFiles SHT_OAT_PATCH section to include the patches.
- if (add_patches) {
- std::string err;
- // TODO we are mapping in the contents of this file twice. We should be able
- // to do it only once, which would be better.
- std::unique_ptr<ElfFile> file(ElfFile::Open(elf_file, true, false, &err));
- if (file == nullptr) {
- LOG(ERROR) << err;
- }
- Elf32_Shdr* shdr = file->FindSectionByName(".oat_patches");
- if (shdr != nullptr) {
- CHECK_EQ(shdr, file->FindSectionByType(SHT_OAT_PATCH))
- << "Incorrect type for .oat_patches section";
- CHECK_LE(patches.size() * sizeof(uintptr_t), shdr->sh_size)
- << "We got more patches than anticipated";
- CHECK_LE(reinterpret_cast<uintptr_t>(file->Begin()) + shdr->sh_offset + shdr->sh_size,
- reinterpret_cast<uintptr_t>(file->End())) << "section is too large";
- CHECK(shdr == &file->GetSectionHeader(file->GetSectionHeaderNum() - 1) ||
- shdr->sh_offset + shdr->sh_size <= (shdr + 1)->sh_offset)
- << "Section overlaps onto next section";
- // It's mmap'd so we can just memcpy.
- memcpy(file->Begin() + shdr->sh_offset, patches.data(), patches.size()*sizeof(uintptr_t));
- // TODO We should fill in the newly empty space between the last patch and the start of the
- // next section by moving the following sections down if possible.
- shdr->sh_size = patches.size() * sizeof(uintptr_t);
- } else {
- LOG(ERROR) << "Unable to find section header for SHT_OAT_PATCH";
- }
- }
-}
-
-void ImageWriter::SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value,
- uintptr_t* patched_ptr) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- const void* quick_oat_code = class_linker->GetQuickOatCodeFor(patch->GetDexFile(),
- patch->GetReferrerClassDefIdx(),
- patch->GetReferrerMethodIdx());
- OatHeader& oat_header = const_cast<OatHeader&>(oat_file_->GetOatHeader());
- // TODO: make this Thumb2 specific
- uint8_t* base = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(quick_oat_code) & ~0x1);
- uint32_t* patch_location = reinterpret_cast<uint32_t*>(base + patch->GetLiteralOffset());
- if (kIsDebugBuild) {
- if (patch->IsCall()) {
- const CompilerDriver::CallPatchInformation* cpatch = patch->AsCall();
- const DexFile::MethodId& id = cpatch->GetTargetDexFile()->GetMethodId(cpatch->GetTargetMethodIdx());
- uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
- uint32_t actual = *patch_location;
- CHECK(actual == expected || actual == value) << std::hex
- << "actual=" << actual
- << "expected=" << expected
- << "value=" << value;
- }
- if (patch->IsType()) {
- const CompilerDriver::TypePatchInformation* tpatch = patch->AsType();
- const DexFile::TypeId& id = tpatch->GetDexFile().GetTypeId(tpatch->GetTargetTypeIdx());
- uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
- uint32_t actual = *patch_location;
- CHECK(actual == expected || actual == value) << std::hex
- << "actual=" << actual
- << "expected=" << expected
- << "value=" << value;
- }
- }
- *patch_location = value;
- oat_header.UpdateChecksum(patch_location, sizeof(value));
-
- uintptr_t loc = reinterpret_cast<uintptr_t>(patch_location) -
- (reinterpret_cast<uintptr_t>(oat_file_->Begin()) + oat_header.GetExecutableOffset());
- CHECK_GT(reinterpret_cast<uintptr_t>(patch_location),
- reinterpret_cast<uintptr_t>(oat_file_->Begin()) + oat_header.GetExecutableOffset());
- CHECK_LT(loc, oat_file_->Size() - oat_header.GetExecutableOffset());
-
- *patched_ptr = loc;
+ image_header->SetOatChecksum(oat_header->GetChecksum());
}
} // namespace art
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index cf5bc93..e8bcf7f 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -70,6 +70,11 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
size_t GetImageOffset(mirror::Object* object) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static void* GetImageAddressCallback(void* writer, mirror::Object* obj)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return reinterpret_cast<ImageWriter*>(writer)->GetImageAddress(obj);
+ }
+
mirror::Object* GetImageAddress(mirror::Object* object) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (object == NULL) {
@@ -159,9 +164,6 @@
// Patches references in OatFile to expect runtime addresses.
void PatchOatCodeAndMethods(File* elf_file)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value,
- uintptr_t* patched_location)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const CompilerDriver& compiler_driver_;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 84f0b3c..11d1728 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -120,6 +120,7 @@
OatWriter oat_writer(class_linker->GetBootClassPath(),
42U,
4096U,
+ 0,
compiler_driver_.get(),
&timings,
&key_value_store);
@@ -183,7 +184,7 @@
TEST_F(OatTest, OatHeaderSizeCheck) {
// If this test is failing and you have to update these constants,
// it is time to update OatHeader::kOatVersion
- EXPECT_EQ(80U, sizeof(OatHeader));
+ EXPECT_EQ(84U, sizeof(OatHeader));
EXPECT_EQ(8U, sizeof(OatMethodOffsets));
EXPECT_EQ(24U, sizeof(OatQuickMethodHeader));
EXPECT_EQ(79 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 63a3c8c..22f36f4 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -50,6 +50,7 @@
OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files,
uint32_t image_file_location_oat_checksum,
uintptr_t image_file_location_oat_begin,
+ int32_t image_patch_delta,
const CompilerDriver* compiler,
TimingLogger* timings,
SafeMap<std::string, std::string>* key_value_store)
@@ -57,6 +58,7 @@
dex_files_(&dex_files),
image_file_location_oat_checksum_(image_file_location_oat_checksum),
image_file_location_oat_begin_(image_file_location_oat_begin),
+ image_patch_delta_(image_patch_delta),
key_value_store_(key_value_store),
oat_header_(NULL),
size_dex_file_alignment_(0),
@@ -126,6 +128,7 @@
CHECK_EQ(dex_files_->size(), oat_dex_files_.size());
CHECK_EQ(compiler->IsImage(),
key_value_store_->find(OatHeader::kImageLocationKey) == key_value_store_->end());
+ CHECK_ALIGNED(image_patch_delta_, kPageSize);
}
OatWriter::~OatWriter() {
@@ -808,6 +811,7 @@
oat_header_->SetExecutableOffset(offset);
size_executable_offset_alignment_ = offset - old_offset;
if (compiler_driver_->IsImage()) {
+ CHECK_EQ(image_patch_delta_, 0);
InstructionSet instruction_set = compiler_driver_->GetInstructionSet();
#define DO_TRAMPOLINE(field, fn_name) \
@@ -840,6 +844,7 @@
oat_header_->SetQuickImtConflictTrampolineOffset(0);
oat_header_->SetQuickResolutionTrampolineOffset(0);
oat_header_->SetQuickToInterpreterBridgeOffset(0);
+ oat_header_->SetImagePatchDelta(image_patch_delta_);
}
return offset;
}
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 3d34956..945048e 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -79,6 +79,7 @@
OatWriter(const std::vector<const DexFile*>& dex_files,
uint32_t image_file_location_oat_checksum,
uintptr_t image_file_location_oat_begin,
+ int32_t image_patch_delta,
const CompilerDriver* compiler,
TimingLogger* timings,
SafeMap<std::string, std::string>* key_value_store);
@@ -253,6 +254,7 @@
// dependencies on the image.
uint32_t image_file_location_oat_checksum_;
uintptr_t image_file_location_oat_begin_;
+ int32_t image_patch_delta_;
// data to write
SafeMap<std::string, std::string>* key_value_store_;
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index f594129..1f0b361 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -34,6 +34,37 @@
namespace art {
+/**
+ * Helper class to add HTemporary instructions. This class is used when
+ * converting a DEX instruction to multiple HInstruction, and where those
+ * instructions do not die at the following instruction, but instead spans
+ * multiple instructions.
+ */
+class Temporaries : public ValueObject {
+ public:
+ Temporaries(HGraph* graph, size_t count) : graph_(graph), count_(count), index_(0) {
+ graph_->UpdateNumberOfTemporaries(count_);
+ }
+
+ void Add(HInstruction* instruction) {
+ // We currently only support vreg size temps.
+ DCHECK(instruction->GetType() != Primitive::kPrimLong
+ && instruction->GetType() != Primitive::kPrimDouble);
+ HInstruction* temp = new (graph_->GetArena()) HTemporary(index_++);
+ instruction->GetBlock()->AddInstruction(temp);
+ DCHECK(temp->GetPrevious() == instruction);
+ }
+
+ private:
+ HGraph* const graph_;
+
+ // The total number of temporaries that will be used.
+ const size_t count_;
+
+ // Current index in the temporary stack, updated by `Add`.
+ size_t index_;
+};
+
static bool IsTypeSupported(Primitive::Type type) {
return type != Primitive::kPrimFloat && type != Primitive::kPrimDouble;
}
@@ -308,9 +339,13 @@
arena_, number_of_arguments, return_type, dex_offset, method_idx);
size_t start_index = 0;
+ Temporaries temps(graph_, is_instance_call ? 1 : 0);
if (is_instance_call) {
HInstruction* arg = LoadLocal(is_range ? register_index : args[0], Primitive::kPrimNot);
- invoke->SetArgumentAt(0, arg);
+ HNullCheck* null_check = new (arena_) HNullCheck(arg, dex_offset);
+ current_block_->AddInstruction(null_check);
+ temps.Add(null_check);
+ invoke->SetArgumentAt(0, null_check);
start_index = 1;
}
@@ -343,37 +378,6 @@
return true;
}
-/**
- * Helper class to add HTemporary instructions. This class is used when
- * converting a DEX instruction to multiple HInstruction, and where those
- * instructions do not die at the following instruction, but instead spans
- * multiple instructions.
- */
-class Temporaries : public ValueObject {
- public:
- Temporaries(HGraph* graph, size_t count) : graph_(graph), count_(count), index_(0) {
- graph_->UpdateNumberOfTemporaries(count_);
- }
-
- void Add(HInstruction* instruction) {
- // We currently only support vreg size temps.
- DCHECK(instruction->GetType() != Primitive::kPrimLong
- && instruction->GetType() != Primitive::kPrimDouble);
- HInstruction* temp = new (graph_->GetArena()) HTemporary(index_++);
- instruction->GetBlock()->AddInstruction(temp);
- DCHECK(temp->GetPrevious() == instruction);
- }
-
- private:
- HGraph* const graph_;
-
- // The total number of temporaries that will be used.
- const size_t count_;
-
- // Current index in the temporary stack, updated by `Add`.
- size_t index_;
-};
-
bool HGraphBuilder::BuildFieldAccess(const Instruction& instruction,
uint32_t dex_offset,
bool is_put) {
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index e0db0f1..bd8c27e 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -30,13 +30,16 @@
namespace art {
-void CodeGenerator::CompileBaseline(CodeAllocator* allocator) {
+void CodeGenerator::CompileBaseline(CodeAllocator* allocator, bool is_leaf) {
const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock());
DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1)));
block_labels_.SetSize(blocks.Size());
DCHECK_EQ(frame_size_, kUninitializedFrameSize);
+ if (!is_leaf) {
+ MarkNotLeaf();
+ }
ComputeFrameSize(GetGraph()->GetMaximumNumberOfOutVRegs()
+ GetGraph()->GetNumberOfLocalVRegs()
+ GetGraph()->GetNumberOfTemporaries()
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 18e3e5a..b31c3a3 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -70,7 +70,7 @@
public:
// Compiles the graph to executable instructions. Returns whether the compilation
// succeeded.
- void CompileBaseline(CodeAllocator* allocator);
+ void CompileBaseline(CodeAllocator* allocator, bool is_leaf = false);
void CompileOptimized(CodeAllocator* allocator);
static CodeGenerator* Create(ArenaAllocator* allocator,
HGraph* graph,
@@ -131,6 +131,14 @@
void BuildNativeGCMap(
std::vector<uint8_t>* vector, const DexCompilationUnit& dex_compilation_unit) const;
+ bool IsLeafMethod() const {
+ return is_leaf_;
+ }
+
+ void MarkNotLeaf() {
+ is_leaf_ = false;
+ }
+
protected:
CodeGenerator(HGraph* graph, size_t number_of_registers)
: frame_size_(kUninitializedFrameSize),
@@ -138,7 +146,8 @@
block_labels_(graph->GetArena(), 0),
pc_infos_(graph->GetArena(), 32),
slow_paths_(graph->GetArena(), 8),
- blocked_registers_(graph->GetArena()->AllocArray<bool>(number_of_registers)) {}
+ blocked_registers_(graph->GetArena()->AllocArray<bool>(number_of_registers)),
+ is_leaf_(true) {}
~CodeGenerator() {}
// Register allocation logic.
@@ -171,6 +180,8 @@
// Temporary data structure used when doing register allocation.
bool* const blocked_registers_;
+ bool is_leaf_;
+
DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
};
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 93e7367..90ec6cf 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -24,6 +24,7 @@
#include "utils/assembler.h"
#include "utils/arm/assembler_arm.h"
#include "utils/arm/managed_register_arm.h"
+#include "utils/stack_checks.h"
namespace art {
@@ -33,6 +34,11 @@
namespace arm {
+static constexpr bool kExplicitStackOverflowCheck = false;
+
+static constexpr int kNumberOfPushedRegistersAtEntry = 1 + 2; // LR, R6, R7
+static constexpr int kCurrentMethodStackOffset = 0;
+
#define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())->
class NullCheckSlowPathARM : public SlowPathCode {
@@ -52,6 +58,20 @@
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM);
};
+class StackOverflowCheckSlowPathARM : public SlowPathCode {
+ public:
+ StackOverflowCheckSlowPathARM() {}
+
+ virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ __ Bind(GetEntryLabel());
+ __ LoadFromOffset(kLoadWord, PC, TR,
+ QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowStackOverflow).Int32Value());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathARM);
+};
+
#undef __
#define __ reinterpret_cast<ArmAssembler*>(GetAssembler())->
@@ -83,9 +103,6 @@
return EQ; // Unreachable.
}
-static constexpr int kNumberOfPushedRegistersAtEntry = 1 + 2; // LR, R6, R7
-static constexpr int kCurrentMethodStackOffset = 0;
-
void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const {
stream << ArmManagedRegister::FromCoreRegister(Register(reg));
}
@@ -207,6 +224,22 @@
codegen_(codegen) {}
void CodeGeneratorARM::GenerateFrameEntry() {
+ bool skip_overflow_check = IsLeafMethod() && !IsLargeFrame(GetFrameSize(), InstructionSet::kArm);
+ if (!skip_overflow_check) {
+ if (kExplicitStackOverflowCheck) {
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM();
+ AddSlowPath(slow_path);
+
+ __ LoadFromOffset(kLoadWord, IP, TR, Thread::StackEndOffset<kArmWordSize>().Int32Value());
+ __ cmp(SP, ShifterOperand(IP));
+ __ b(slow_path->GetEntryLabel(), CC);
+ } else {
+ __ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
+ __ ldr(IP, Address(IP, 0));
+ RecordPcInfo(0);
+ }
+ }
+
core_spill_mask_ |= (1 << LR | 1 << R6 | 1 << R7);
__ PushList(1 << LR | 1 << R6 | 1 << R7);
@@ -720,6 +753,7 @@
}
void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) {
+ codegen_->MarkNotLeaf();
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
locations->AddTemp(ArmCoreLocation(R0));
@@ -785,6 +819,7 @@
__ blx(LR);
codegen_->RecordPcInfo(invoke->GetDexPc());
+ DCHECK(!codegen_->IsLeafMethod());
}
void LocationsBuilderARM::VisitAdd(HAdd* add) {
@@ -923,6 +958,7 @@
};
void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) {
+ codegen_->MarkNotLeaf();
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
InvokeRuntimeCallingConvention calling_convention;
locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(0)));
@@ -941,6 +977,7 @@
__ blx(LR);
codegen_->RecordPcInfo(instruction->GetDexPc());
+ DCHECK(!codegen_->IsLeafMethod());
}
void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index c44b761..f1eb372 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -17,6 +17,7 @@
#include "code_generator_x86.h"
#include "gc/accounting/card_table.h"
#include "utils/assembler.h"
+#include "utils/stack_checks.h"
#include "utils/x86/assembler_x86.h"
#include "utils/x86/managed_register_x86.h"
@@ -33,6 +34,11 @@
namespace x86 {
+static constexpr bool kExplicitStackOverflowCheck = false;
+
+static constexpr int kNumberOfPushedRegistersAtEntry = 1;
+static constexpr int kCurrentMethodStackOffset = 0;
+
#define __ reinterpret_cast<X86Assembler*>(codegen->GetAssembler())->
class NullCheckSlowPathX86 : public SlowPathCode {
@@ -50,6 +56,21 @@
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86);
};
+class StackOverflowCheckSlowPathX86 : public SlowPathCode {
+ public:
+ StackOverflowCheckSlowPathX86() {}
+
+ virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ __ Bind(GetEntryLabel());
+ __ addl(ESP,
+ Immediate(codegen->GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize));
+ __ fs()->jmp(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowStackOverflow)));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86);
+};
+
#undef __
#define __ reinterpret_cast<X86Assembler*>(GetAssembler())->
@@ -67,9 +88,6 @@
return kEqual;
}
-static constexpr int kNumberOfPushedRegistersAtEntry = 1;
-static constexpr int kCurrentMethodStackOffset = 0;
-
void CodeGeneratorX86::DumpCoreRegister(std::ostream& stream, int reg) const {
stream << X86ManagedRegister::FromCpuRegister(Register(reg));
}
@@ -186,6 +204,20 @@
// The return PC has already been pushed on the stack.
__ subl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize));
+
+ bool skip_overflow_check = IsLeafMethod() && !IsLargeFrame(GetFrameSize(), InstructionSet::kX86);
+ if (!skip_overflow_check) {
+ if (kExplicitStackOverflowCheck) {
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86();
+ AddSlowPath(slow_path);
+
+ __ fs()->cmpl(ESP, Address::Absolute(Thread::StackEndOffset<kX86WordSize>()));
+ __ j(kLess, slow_path->GetEntryLabel());
+ } else {
+ __ testl(EAX, Address(ESP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86))));
+ }
+ }
+
__ movl(Address(ESP, kCurrentMethodStackOffset), EAX);
}
@@ -682,6 +714,7 @@
}
void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) {
+ codegen_->MarkNotLeaf();
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
locations->AddTemp(X86CpuLocation(EAX));
@@ -739,6 +772,7 @@
// (temp + offset_of_quick_compiled_code)()
__ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()));
+ DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke->GetDexPc());
}
@@ -889,6 +923,7 @@
}
void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) {
+ codegen_->MarkNotLeaf();
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
locations->SetOut(X86CpuLocation(EAX));
InvokeRuntimeCallingConvention calling_convention;
@@ -906,6 +941,7 @@
Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocObjectWithAccessCheck)));
codegen_->RecordPcInfo(instruction->GetDexPc());
+ DCHECK(!codegen_->IsLeafMethod());
}
void LocationsBuilderX86::VisitParameterValue(HParameterValue* instruction) {
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 9c12771..b7c2ad8 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -126,7 +126,7 @@
class CodeGeneratorX86 : public CodeGenerator {
public:
explicit CodeGeneratorX86(HGraph* graph);
- virtual ~CodeGeneratorX86() { }
+ virtual ~CodeGeneratorX86() {}
virtual void GenerateFrameEntry() OVERRIDE;
virtual void GenerateFrameExit() OVERRIDE;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index d20dff0..641e8e1 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -23,6 +23,7 @@
#include "mirror/object_reference.h"
#include "thread.h"
#include "utils/assembler.h"
+#include "utils/stack_checks.h"
#include "utils/x86_64/assembler_x86_64.h"
#include "utils/x86_64/managed_register_x86_64.h"
@@ -34,6 +35,15 @@
namespace x86_64 {
+static constexpr bool kExplicitStackOverflowCheck = true;
+
+// Some x86_64 instructions require a register to be available as temp.
+static constexpr Register TMP = R11;
+
+static constexpr int kNumberOfPushedRegistersAtEntry = 1;
+static constexpr int kCurrentMethodStackOffset = 0;
+
+
#define __ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler())->
class NullCheckSlowPathX86_64 : public SlowPathCode {
@@ -42,7 +52,8 @@
virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
__ Bind(GetEntryLabel());
- __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowNullPointer), true));
+ __ gs()->call(
+ Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowNullPointer), true));
codegen->RecordPcInfo(dex_pc_);
}
@@ -51,6 +62,22 @@
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86_64);
};
+class StackOverflowCheckSlowPathX86_64 : public SlowPathCode {
+ public:
+ StackOverflowCheckSlowPathX86_64() {}
+
+ virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ __ Bind(GetEntryLabel());
+ __ addq(CpuRegister(RSP),
+ Immediate(codegen->GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize));
+ __ gs()->jmp(
+ Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowStackOverflow), true));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86_64);
+};
+
#undef __
#define __ reinterpret_cast<X86_64Assembler*>(GetAssembler())->
@@ -68,12 +95,6 @@
return kEqual;
}
-// Some x86_64 instructions require a register to be available as temp.
-static constexpr Register TMP = R11;
-
-static constexpr int kNumberOfPushedRegistersAtEntry = 1;
-static constexpr int kCurrentMethodStackOffset = 0;
-
void CodeGeneratorX86_64::DumpCoreRegister(std::ostream& stream, int reg) const {
stream << X86_64ManagedRegister::FromCpuRegister(Register(reg));
}
@@ -148,7 +169,26 @@
core_spill_mask_ |= (1 << kFakeReturnRegister);
// The return PC has already been pushed on the stack.
- __ subq(CpuRegister(RSP), Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize));
+ __ subq(CpuRegister(RSP),
+ Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize));
+
+ bool skip_overflow_check = IsLeafMethod()
+ && !IsLargeFrame(GetFrameSize(), InstructionSet::kX86_64);
+
+ if (!skip_overflow_check) {
+ if (kExplicitStackOverflowCheck) {
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86_64();
+ AddSlowPath(slow_path);
+
+ __ gs()->cmpq(CpuRegister(RSP),
+ Address::Absolute(Thread::StackEndOffset<kX86_64WordSize>(), true));
+ __ j(kLess, slow_path->GetEntryLabel());
+ } else {
+ __ testq(CpuRegister(RAX), Address(
+ CpuRegister(RSP), -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86_64))));
+ }
+ }
+
__ movl(Address(CpuRegister(RSP), kCurrentMethodStackOffset), CpuRegister(RDI));
}
@@ -619,6 +659,7 @@
}
void LocationsBuilderX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
+ codegen_->MarkNotLeaf();
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
locations->AddTemp(X86_64CpuLocation(RDI));
@@ -673,6 +714,7 @@
// (temp + offset_of_quick_compiled_code)()
__ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue()));
+ DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke->GetDexPc());
}
@@ -809,6 +851,7 @@
}
void LocationsBuilderX86_64::VisitNewInstance(HNewInstance* instruction) {
+ codegen_->MarkNotLeaf();
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
locations->SetOut(X86_64CpuLocation(RAX));
instruction->SetLocations(locations);
@@ -822,6 +865,7 @@
__ gs()->call(Address::Absolute(
QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocObjectWithAccessCheck), true));
+ DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(instruction->GetDexPc());
}
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index bfdc30f..d7ac10d 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -76,19 +76,21 @@
InternalCodeAllocator allocator;
CodeGenerator* codegen = CodeGenerator::Create(&arena, graph, kX86);
- codegen->CompileBaseline(&allocator);
+ // We avoid doing a stack overflow check that requires the runtime being setup,
+ // by making sure the compiler knows the methods we are running are leaf methods.
+ codegen->CompileBaseline(&allocator, true);
#if defined(__i386__)
Run(allocator, *codegen, has_result, expected);
#endif
codegen = CodeGenerator::Create(&arena, graph, kArm);
- codegen->CompileBaseline(&allocator);
+ codegen->CompileBaseline(&allocator, true);
#if defined(__arm__)
Run(allocator, *codegen, has_result, expected);
#endif
codegen = CodeGenerator::Create(&arena, graph, kX86_64);
- codegen->CompileBaseline(&allocator);
+ codegen->CompileBaseline(&allocator, true);
#if defined(__x86_64__)
Run(allocator, *codegen, has_result, expected);
#endif
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 534783a..3312f8f 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -171,7 +171,7 @@
}
out.close();
- char cmd[256];
+ char cmd[1024];
// Assemble the .S
snprintf(cmd, sizeof(cmd), "%s%sas %s -o %s.o", toolsdir.c_str(), TOOL_PREFIX, filename, filename);
diff --git a/compiler/utils/stack_checks.h b/compiler/utils/stack_checks.h
new file mode 100644
index 0000000..63adbc2
--- /dev/null
+++ b/compiler/utils/stack_checks.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 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_UTILS_STACK_CHECKS_H_
+#define ART_COMPILER_UTILS_STACK_CHECKS_H_
+
+#include "instruction_set.h"
+
+namespace art {
+
+// Size of a frame that we definitely consider large. Anything larger than this should
+// definitely get a stack overflow check.
+static constexpr size_t kLargeFrameSize = 2 * KB;
+
+// Size of a frame that should be small. Anything leaf method smaller than this should run
+// without a stack overflow check.
+// The constant is from experience with frameworks code.
+static constexpr size_t kSmallFrameSize = 1 * KB;
+
+// Determine whether a frame is small or large, used in the decision on whether to elide a
+// stack overflow check on method entry.
+//
+// A frame is considered large when it's either above kLargeFrameSize, or a quarter of the
+// overflow-usable stack space.
+static inline bool IsLargeFrame(size_t size, InstructionSet isa) {
+ return size >= kLargeFrameSize || size >= GetStackOverflowReservedBytes(isa) / 4;
+}
+
+} // namespace art
+
+#endif // ART_COMPILER_UTILS_STACK_CHECKS_H_
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index 56c6536..b6a5c20 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -806,6 +806,13 @@
}
+void X86Assembler::testl(Register reg, const Address& address) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x85);
+ EmitOperand(reg, address);
+}
+
+
void X86Assembler::testl(Register reg, const Immediate& immediate) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
// For registers that have a byte variant (EAX, EBX, ECX, and EDX)
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index 2fc6049..ce20768 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -346,6 +346,7 @@
void testl(Register reg1, Register reg2);
void testl(Register reg, const Immediate& imm);
+ void testl(Register reg1, const Address& address);
void andl(Register dst, const Immediate& imm);
void andl(Register dst, Register src);
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 1dbef95..0d14376 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -950,6 +950,14 @@
}
+void X86_64Assembler::testq(CpuRegister reg, const Address& address) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitRex64(reg);
+ EmitUint8(0x85);
+ EmitOperand(reg.LowBits(), address);
+}
+
+
void X86_64Assembler::andl(CpuRegister dst, CpuRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitOptionalRex32(dst, src);
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index e988029..1d6655c 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -391,6 +391,8 @@
void testl(CpuRegister reg1, CpuRegister reg2);
void testl(CpuRegister reg, const Immediate& imm);
+ void testq(CpuRegister reg, const Address& address);
+
void andl(CpuRegister dst, const Immediate& imm);
void andl(CpuRegister dst, CpuRegister src);
void andq(CpuRegister dst, const Immediate& imm);
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 6d861d4..d6501a1 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -45,6 +45,7 @@
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "elf_fixup.h"
+#include "elf_patcher.h"
#include "elf_stripper.h"
#include "gc/space/image_space.h"
#include "gc/space/space-inl.h"
@@ -324,11 +325,28 @@
return ReadImageClasses(image_classes_stream);
}
+ bool PatchOatCode(const CompilerDriver* compiler_driver, File* oat_file,
+ const std::string& oat_location, std::string* error_msg) {
+ // We asked to include patch information but we are not making an image. We need to fix
+ // everything up manually.
+ std::unique_ptr<ElfFile> elf_file(ElfFile::Open(oat_file, PROT_READ|PROT_WRITE,
+ MAP_SHARED, error_msg));
+ if (elf_file.get() == NULL) {
+ LOG(ERROR) << error_msg;
+ return false;
+ }
+ {
+ ReaderMutexLock mu(Thread::Current(), *Locks::mutator_lock_);
+ return ElfPatcher::Patch(compiler_driver, elf_file.get(), oat_location, error_msg);
+ }
+ }
+
const CompilerDriver* CreateOatFile(const std::string& boot_image_option,
const std::string& android_root,
bool is_host,
const std::vector<const DexFile*>& dex_files,
File* oat_file,
+ const std::string& oat_location,
const std::string& bitcode_filename,
bool image,
std::unique_ptr<CompilerDriver::DescriptorSet>& image_classes,
@@ -380,6 +398,7 @@
std::string image_file_location;
uint32_t image_file_location_oat_checksum = 0;
uintptr_t image_file_location_oat_data_begin = 0;
+ int32_t image_patch_delta = 0;
if (!driver->IsImage()) {
TimingLogger::ScopedTiming t3("Loading image checksum", &timings);
gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
@@ -387,6 +406,7 @@
image_file_location_oat_data_begin =
reinterpret_cast<uintptr_t>(image_space->GetImageHeader().GetOatDataBegin());
image_file_location = image_space->GetImageFilename();
+ image_patch_delta = image_space->GetImageHeader().GetPatchDelta();
}
if (!image_file_location.empty()) {
@@ -395,6 +415,7 @@
OatWriter oat_writer(dex_files, image_file_location_oat_checksum,
image_file_location_oat_data_begin,
+ image_patch_delta,
driver.get(),
&timings,
key_value_store);
@@ -405,6 +426,16 @@
return nullptr;
}
+ if (!driver->IsImage() && driver->GetCompilerOptions().GetIncludePatchInformation()) {
+ t2.NewTiming("Patching ELF");
+ std::string error_msg;
+ if (!PatchOatCode(driver.get(), oat_file, oat_location, &error_msg)) {
+ LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
+ LOG(ERROR) << "Error was: " << error_msg;
+ return nullptr;
+ }
+ }
+
return driver.release();
}
@@ -1361,6 +1392,7 @@
is_host,
dex_files,
oat_file.get(),
+ oat_location,
bitcode_filename,
image,
image_classes,
@@ -1370,7 +1402,6 @@
compiler_phases_timings,
profile_file,
key_value_store.get()));
-
if (compiler.get() == nullptr) {
LOG(ERROR) << "Failed to create oat file: " << oat_location;
return EXIT_FAILURE;
@@ -1420,9 +1451,9 @@
// memory mapped so we could predict where its contents were based
// on the file size. Now that it is an ELF file, we need to inspect
// the ELF file to understand the in memory segment layout including
- // where the oat header is located within. ImageWriter's
- // PatchOatCodeAndMethods uses the PatchInformation from the
- // Compiler to touch up absolute references in the oat file.
+ // where the oat header is located within. ElfPatcher's Patch method
+ // uses the PatchInformation from the Compiler to touch up absolute
+ // references in the oat file.
//
// 3. We fixup the ELF program headers so that dlopen will try to
// load the .so at the desired location at runtime by offsetting the
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index b8f20f3..068a450 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -165,6 +165,8 @@
GetQuickToInterpreterBridgeOffset);
#undef DUMP_OAT_HEADER_OFFSET
+ os << "IMAGE PATCH DELTA:\n" << oat_header.GetImagePatchDelta();
+
os << "IMAGE FILE LOCATION OAT CHECKSUM:\n";
os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatChecksum());
@@ -771,6 +773,8 @@
os << "OAT FILE END:" << reinterpret_cast<void*>(image_header_.GetOatFileEnd()) << "\n\n";
+ os << "PATCH DELTA:" << image_header_.GetPatchDelta() << "\n\n";
+
{
os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
Indenter indent1_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
@@ -819,10 +823,13 @@
os << "OAT LOCATION: " << oat_location;
os << "\n";
std::string error_msg;
- const OatFile* oat_file = class_linker->FindOatFileFromOatLocation(oat_location, &error_msg);
- if (oat_file == NULL) {
- os << "NOT FOUND: " << error_msg << "\n";
- return;
+ const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
+ if (oat_file == nullptr) {
+ oat_file = OatFile::Open(oat_location, oat_location, NULL, false, &error_msg);
+ if (oat_file == nullptr) {
+ os << "NOT FOUND: " << error_msg << "\n";
+ return;
+ }
}
os << "\n";
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 85b4e6d..55d582f 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -17,11 +17,14 @@
#include <stdio.h>
#include <stdlib.h>
+#include <sys/file.h>
#include <sys/stat.h>
+#include <unistd.h>
#include <string>
#include <vector>
+#include "base/scoped_flock.h"
#include "base/stringpiece.h"
#include "base/stringprintf.h"
#include "elf_utils.h"
@@ -125,7 +128,7 @@
delta, timings);
t.NewTiming("Patching files");
if (!p.PatchImage()) {
- LOG(INFO) << "Failed to patch image file " << input_image->GetPath();
+ LOG(ERROR) << "Failed to patch image file " << input_image->GetPath();
return false;
}
@@ -216,11 +219,11 @@
delta, timings);
t.NewTiming("Patching files");
if (!p.PatchElf()) {
- LOG(INFO) << "Failed to patch oat file " << input_oat->GetPath();
+ LOG(ERROR) << "Failed to patch oat file " << input_oat->GetPath();
return false;
}
if (!p.PatchImage()) {
- LOG(INFO) << "Failed to patch image file " << input_image->GetPath();
+ LOG(ERROR) << "Failed to patch image file " << input_image->GetPath();
return false;
}
@@ -236,6 +239,12 @@
bool PatchOat::WriteElf(File* out) {
TimingLogger::ScopedTiming t("Writing Elf File", timings_);
+ std::string error_msg;
+
+ // Lock the output file.
+ ScopedFlock flock;
+ flock.Init(out, &error_msg);
+
CHECK(oat_file_.get() != nullptr);
CHECK(out != nullptr);
size_t expect = oat_file_->Size();
@@ -250,6 +259,12 @@
bool PatchOat::WriteImage(File* out) {
TimingLogger::ScopedTiming t("Writing image File", timings_);
+ std::string error_msg;
+
+ // Lock the output file.
+ ScopedFlock flock;
+ flock.Init(out, &error_msg);
+
CHECK(image_ != nullptr);
CHECK(out != nullptr);
size_t expect = image_->Size();
@@ -437,19 +452,50 @@
return true;
}
+bool PatchOat::PatchOatHeader() {
+ Elf32_Shdr *rodata_sec = oat_file_->FindSectionByName(".rodata");
+ if (rodata_sec == nullptr) {
+ return false;
+ }
+ OatHeader* oat_header = reinterpret_cast<OatHeader*>(oat_file_->Begin() + rodata_sec->sh_offset);
+ if (!oat_header->IsValid()) {
+ LOG(ERROR) << "Elf file " << oat_file_->GetFile().GetPath() << " has an invalid oat header";
+ return false;
+ }
+ oat_header->RelocateOat(delta_);
+ return true;
+}
+
bool PatchOat::PatchElf() {
- TimingLogger::ScopedTiming t("Fixup Elf Headers", timings_);
+ TimingLogger::ScopedTiming t("Fixup Elf Text Section", timings_);
+ if (!PatchTextSection()) {
+ return false;
+ }
+
+ if (!PatchOatHeader()) {
+ return false;
+ }
+
+ bool need_fixup = false;
+ t.NewTiming("Fixup Elf Headers");
// Fixup Phdr's
for (unsigned int i = 0; i < oat_file_->GetProgramHeaderNum(); i++) {
Elf32_Phdr& hdr = oat_file_->GetProgramHeader(i);
- if (hdr.p_vaddr != 0) {
+ if (hdr.p_vaddr != 0 && hdr.p_vaddr != hdr.p_offset) {
+ need_fixup = true;
hdr.p_vaddr += delta_;
}
- if (hdr.p_paddr != 0) {
+ if (hdr.p_paddr != 0 && hdr.p_paddr != hdr.p_offset) {
+ need_fixup = true;
hdr.p_paddr += delta_;
}
}
- // Fixup Shdr's
+ if (!need_fixup) {
+ // This was never passed through ElfFixup so all headers/symbols just have their offset as
+ // their addr. Therefore we do not need to update these parts.
+ return true;
+ }
+ t.NewTiming("Fixup Section Headers");
for (unsigned int i = 0; i < oat_file_->GetSectionHeaderNum(); i++) {
Elf32_Shdr& hdr = oat_file_->GetSectionHeader(i);
if (hdr.sh_addr != 0) {
@@ -457,7 +503,7 @@
}
}
- // Fixup Dynamics.
+ t.NewTiming("Fixup Dynamics");
for (Elf32_Word i = 0; i < oat_file_->GetDynamicNum(); i++) {
Elf32_Dyn& dyn = oat_file_->GetDynamic(i);
if (IsDynamicSectionPointer(dyn.d_tag, oat_file_->GetHeader().e_machine)) {
@@ -481,12 +527,6 @@
}
}
- t.NewTiming("Fixup Elf Text Section");
- // Fixup text
- if (!PatchTextSection()) {
- return false;
- }
-
return true;
}
@@ -511,7 +551,7 @@
bool PatchOat::PatchTextSection() {
Elf32_Shdr* patches_sec = oat_file_->FindSectionByName(".oat_patches");
if (patches_sec == nullptr) {
- LOG(INFO) << ".oat_patches section not found. Aborting patch";
+ LOG(ERROR) << ".oat_patches section not found. Aborting patch";
return false;
}
DCHECK(CheckOatFile()) << "Oat file invalid";
@@ -614,7 +654,8 @@
UsageError("");
UsageError(" --patched-image-location=<file.art>: Use the same patch delta as was used to");
UsageError(" patch the given image location. If used one must also specify the");
- UsageError(" --instruction-set flag.");
+ UsageError(" --instruction-set flag. It will search for this image in the same way that");
+ UsageError(" is done when loading one.");
UsageError("");
UsageError(" --dump-timings: dump out patch timing information");
UsageError("");
@@ -909,7 +950,25 @@
if (!isa_set) {
Usage("specifying a location requires specifying an instruction set");
}
- patched_image_filename = GetSystemImageFilename(patched_image_location.c_str(), isa);
+ std::string system_filename;
+ bool has_system = false;
+ std::string cache_filename;
+ bool has_cache = false;
+ bool has_android_data_unused = false;
+ if (!gc::space::ImageSpace::FindImageFilename(patched_image_location.c_str(), isa,
+ &system_filename, &has_system, &cache_filename,
+ &has_android_data_unused, &has_cache)) {
+ Usage("Unable to determine image file for location %s", patched_image_location.c_str());
+ }
+ if (has_cache) {
+ patched_image_filename = cache_filename;
+ } else if (has_system) {
+ LOG(WARNING) << "Only image file found was in /system for image location "
+ << patched_image_location;
+ patched_image_filename = system_filename;
+ } else {
+ Usage("Unable to determine image file for location %s", patched_image_location.c_str());
+ }
if (debug) {
LOG(INFO) << "Using patched-image-file " << patched_image_filename;
}
@@ -969,6 +1028,14 @@
if (output_oat_fd != -1) {
output_oat.reset(new File(output_oat_fd, output_oat_filename));
+ } else if (output_oat_filename == input_oat_filename) {
+ // This could be a wierd situation, since we'd be writting from an mmap'd copy of this file.
+ // Lets just unlink it.
+ if (0 != unlink(input_oat_filename.c_str())) {
+ PLOG(ERROR) << "Could not unlink " << input_oat_filename << " to make room for output";
+ return false;
+ }
+ output_oat.reset(OS::CreateEmptyFile(output_oat_filename.c_str()));
} else {
CHECK(!output_oat_filename.empty());
output_oat.reset(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out));
@@ -994,7 +1061,9 @@
};
if (debug) {
- LOG(INFO) << "moving offset by " << base_delta << " (0x" << std::hex << base_delta << ") bytes";
+ LOG(INFO) << "moving offset by " << base_delta
+ << " (0x" << std::hex << base_delta << ") bytes or "
+ << std::dec << (base_delta/kPageSize) << " pages.";
}
bool ret;
@@ -1011,6 +1080,7 @@
ret = PatchOat::Patch(input_image_location, base_delta, output_image.get(), isa, &timings);
}
cleanup(ret);
+ sync();
return (ret) ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index a63e6f4..6960d3b 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -79,6 +79,7 @@
// Patches oat in place, modifying the oat_file given to the constructor.
bool PatchElf();
bool PatchTextSection();
+ bool PatchOatHeader();
bool PatchSymbols(Elf32_Shdr* section);
bool PatchImage() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc
index e22c56e..48582f4 100644
--- a/runtime/arch/arm/fault_handler_arm.cc
+++ b/runtime/arch/arm/fault_handler_arm.cc
@@ -61,7 +61,7 @@
// get the method from the top of the stack. However it's in r0.
uintptr_t* fault_addr = reinterpret_cast<uintptr_t*>(sc->fault_address);
uintptr_t* overflow_addr = reinterpret_cast<uintptr_t*>(
- reinterpret_cast<uint8_t*>(*out_sp) - kArmStackOverflowReservedBytes);
+ reinterpret_cast<uint8_t*>(*out_sp) - GetStackOverflowReservedBytes(kArm));
if (overflow_addr == fault_addr) {
*out_method = reinterpret_cast<mirror::ArtMethod*>(sc->arm_r0);
} else {
@@ -192,7 +192,7 @@
VLOG(signals) << "checking for stack overflow, sp: " << std::hex << sp <<
", fault_addr: " << fault_addr;
- uintptr_t overflow_addr = sp - kArmStackOverflowReservedBytes;
+ uintptr_t overflow_addr = sp - GetStackOverflowReservedBytes(kArm);
Thread* self = reinterpret_cast<Thread*>(sc->arm_r9);
CHECK_EQ(self, Thread::Current());
diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc
index 351de3d..bf091d0 100644
--- a/runtime/base/scoped_flock.cc
+++ b/runtime/base/scoped_flock.cc
@@ -58,6 +58,22 @@
}
}
+bool ScopedFlock::Init(File* file, std::string* error_msg) {
+ file_.reset(new File(dup(file->Fd())));
+ if (file_->Fd() == -1) {
+ file_.reset();
+ *error_msg = StringPrintf("Failed to duplicate open file '%s': %s",
+ file->GetPath().c_str(), strerror(errno));
+ return false;
+ }
+ if (0 != TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_EX))) {
+ file_.reset();
+ *error_msg = StringPrintf("Failed to lock file '%s': %s", file->GetPath().c_str(), strerror(errno));
+ return false;
+ }
+ return true;
+}
+
File* ScopedFlock::GetFile() {
CHECK(file_.get() != NULL);
return file_.get();
diff --git a/runtime/base/scoped_flock.h b/runtime/base/scoped_flock.h
index f8ed805..08612e3 100644
--- a/runtime/base/scoped_flock.h
+++ b/runtime/base/scoped_flock.h
@@ -37,6 +37,10 @@
// changed (usually due to a new file being created at the same path)
// between attempts to lock it.
bool Init(const char* filename, std::string* error_msg);
+ // Attempt to acquire an exclusive file lock (see flock(2)) on 'file'.
+ // Returns true if the lock could be acquired or false if an error
+ // occured.
+ bool Init(File* file, std::string* error_msg);
// Returns the (locked) file associated with this instance.
File* GetFile();
@@ -45,6 +49,7 @@
bool HasFile();
~ScopedFlock();
+
private:
std::unique_ptr<File> file_;
DISALLOW_COPY_AND_ASSIGN(ScopedFlock);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index c40424b..d1c158b 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -814,6 +814,7 @@
return false;
}
+ // TODO Caller specifically asks for this oat_location. We should honor it. Probably?
open_oat_file.reset(FindOatFileInOatLocationForDexFile(dex_location, dex_location_checksum,
oat_location, &error_msg));
@@ -938,6 +939,13 @@
actual_image_oat_offset);
return nullptr;
}
+ int32_t expected_patch_delta = image_header.GetPatchDelta();
+ int32_t actual_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta();
+ if (expected_patch_delta != actual_patch_delta) {
+ *error_msg = StringPrintf("Failed to find oat file at '%s' with expected patch delta %d, "
+ " found %d", oat_location, expected_patch_delta, actual_patch_delta);
+ return nullptr;
+ }
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
&dex_location_checksum);
if (oat_dex_file == nullptr) {
@@ -998,19 +1006,23 @@
// image header from the image for the right instruction set.
uint32_t image_oat_checksum = 0;
uintptr_t image_oat_data_begin = 0;
- if (instruction_set == kRuntimeISA) {
+ int32_t image_patch_delta = 0;
+ if (instruction_set == Runtime::Current()->GetInstructionSet()) {
const ImageHeader& image_header = image_space->GetImageHeader();
image_oat_checksum = image_header.GetOatChecksum();
image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin());
+ image_patch_delta = image_header.GetPatchDelta();
} else {
std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
image_space->GetImageLocation().c_str(), instruction_set));
image_oat_checksum = image_header->GetOatChecksum();
image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin());
+ image_patch_delta = image_header->GetPatchDelta();
}
const OatHeader& oat_header = oat_file->GetOatHeader();
bool image_check = ((oat_header.GetImageFileLocationOatChecksum() == image_oat_checksum)
- && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin));
+ && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin)
+ && (oat_header.GetImagePatchDelta() == image_patch_delta));
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
&dex_location_checksum);
@@ -1049,16 +1061,11 @@
return false;
}
-const OatFile* ClassLinker::LoadOatFileAndVerifyDexFile(const std::string& oat_file_location,
- const char* dex_location,
- std::string* error_msg,
- bool* open_failed) {
- std::unique_ptr<const OatFile> oat_file(FindOatFileFromOatLocation(oat_file_location, error_msg));
- if (oat_file.get() == nullptr) {
- *open_failed = true;
- return nullptr;
- }
- *open_failed = false;
+bool ClassLinker::VerifyOatWithDexFile(const OatFile* oat_file,
+ const char* dex_location,
+ std::string* error_msg) {
+ CHECK(oat_file != nullptr);
+ CHECK(dex_location != nullptr);
std::unique_ptr<const DexFile> dex_file;
uint32_t dex_location_checksum;
if (!DexFile::GetChecksum(dex_location, &dex_location_checksum, error_msg)) {
@@ -1068,26 +1075,21 @@
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, NULL);
if (oat_dex_file == nullptr) {
*error_msg = StringPrintf("Dex checksum mismatch for location '%s' and failed to find oat "
- "dex file '%s': %s", oat_file_location.c_str(), dex_location,
+ "dex file '%s': %s", oat_file->GetLocation().c_str(), dex_location,
error_msg->c_str());
- return nullptr;
+ return false;
}
dex_file.reset(oat_dex_file->OpenDexFile(error_msg));
} else {
- bool verified = VerifyOatFileChecksums(oat_file.get(), dex_location, dex_location_checksum,
+ bool verified = VerifyOatFileChecksums(oat_file, dex_location, dex_location_checksum,
kRuntimeISA, error_msg);
if (!verified) {
- return nullptr;
+ return false;
}
dex_file.reset(oat_file->GetOatDexFile(dex_location,
&dex_location_checksum)->OpenDexFile(error_msg));
}
-
- if (dex_file.get() != nullptr) {
- return oat_file.release();
- } else {
- return nullptr;
- }
+ return dex_file.get() != nullptr;
}
const OatFile* ClassLinker::FindOatFileContainingDexFileFromDexLocation(
@@ -1097,51 +1099,25 @@
std::vector<std::string>* error_msgs,
bool* obsolete_file_cleanup_failed) {
*obsolete_file_cleanup_failed = false;
- // Look for an existing file next to dex. for example, for
- // /foo/bar/baz.jar, look for /foo/bar/<isa>/baz.odex.
- std::string odex_filename(DexFilenameToOdexFilename(dex_location, isa));
- bool open_failed;
+ bool already_opened = false;
+ std::string dex_location_str(dex_location);
+ std::unique_ptr<const OatFile> oat_file(OpenOatFileFromDexLocation(dex_location_str, isa,
+ &already_opened,
+ obsolete_file_cleanup_failed,
+ error_msgs));
std::string error_msg;
- const OatFile* oat_file = LoadOatFileAndVerifyDexFile(odex_filename, dex_location, &error_msg,
- &open_failed);
- if (oat_file != nullptr) {
- return oat_file;
- }
- if (dex_location_checksum == nullptr) {
- error_msgs->push_back(StringPrintf("Failed to open oat file from %s and no classes.dex found in"
- "%s: %s", odex_filename.c_str(), dex_location,
+ if (oat_file.get() == nullptr) {
+ error_msgs->push_back(StringPrintf("Failed to open oat file from dex location '%s'",
+ dex_location));
+ return nullptr;
+ } else if (!VerifyOatWithDexFile(oat_file.get(), dex_location, &error_msg)) {
+ error_msgs->push_back(StringPrintf("Failed to verify oat file '%s' found for dex location "
+ "'%s': %s", oat_file->GetLocation().c_str(), dex_location,
error_msg.c_str()));
return nullptr;
+ } else {
+ return oat_file.release();
}
-
- std::string cache_error_msg;
- const std::string dalvik_cache(GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA)));
- std::string cache_location(GetDalvikCacheFilenameOrDie(dex_location,
- dalvik_cache.c_str()));
- oat_file = LoadOatFileAndVerifyDexFile(cache_location, dex_location, &cache_error_msg,
- &open_failed);
- if (oat_file != nullptr) {
- return oat_file;
- }
-
- if (!open_failed && TEMP_FAILURE_RETRY(unlink(cache_location.c_str())) != 0) {
- std::string error_msg = StringPrintf("Failed to remove obsolete file from %s when searching"
- "for dex file %s: %s",
- cache_location.c_str(), dex_location, strerror(errno));
- error_msgs->push_back(error_msg);
- VLOG(class_linker) << error_msg;
- // Let the caller know that we couldn't remove the obsolete file.
- // This is a good indication that further writes may fail as well.
- *obsolete_file_cleanup_failed = true;
- }
-
- std::string compound_msg = StringPrintf("Failed to open oat file from %s (error '%s') or %s "
- "(error '%s').", odex_filename.c_str(), error_msg.c_str(),
- cache_location.c_str(), cache_error_msg.c_str());
- VLOG(class_linker) << compound_msg;
- error_msgs->push_back(compound_msg);
-
- return nullptr;
}
const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) {
@@ -1156,6 +1132,277 @@
return nullptr;
}
+const OatFile* ClassLinker::OpenOatFileFromDexLocation(const std::string& dex_location,
+ InstructionSet isa,
+ bool *already_opened,
+ bool *obsolete_file_cleanup_failed,
+ std::vector<std::string>* error_msgs) {
+ // Find out if we've already opened the file
+ const OatFile* ret = nullptr;
+ std::string odex_filename(DexFilenameToOdexFilename(dex_location, isa));
+ ret = FindOpenedOatFileFromOatLocation(odex_filename);
+ if (ret != nullptr) {
+ *already_opened = true;
+ return ret;
+ }
+
+ std::string dalvik_cache;
+ bool have_android_data = false;
+ bool have_dalvik_cache = false;
+ GetDalvikCache(GetInstructionSetString(kRuntimeISA), false, &dalvik_cache,
+ &have_android_data, &have_dalvik_cache);
+ std::string cache_filename;
+ if (have_dalvik_cache) {
+ cache_filename = GetDalvikCacheFilenameOrDie(dex_location.c_str(), dalvik_cache.c_str());
+ ret = FindOpenedOatFileFromOatLocation(cache_filename);
+ if (ret != nullptr) {
+ *already_opened = true;
+ return ret;
+ }
+ } else {
+ // If we need to relocate we should just place odex back where it started.
+ cache_filename = odex_filename;
+ }
+
+ ret = nullptr;
+
+ // We know that neither the odex nor the cache'd version is already in use, if it even exists.
+ //
+ // Now we do the following:
+ // 1) Try and open the odex version
+ // 2) If present, checksum-verified & relocated correctly return it
+ // 3) Close the odex version to free up its address space.
+ // 4) Try and open the cache version
+ // 5) If present, checksum-verified & relocated correctly return it
+ // 6) Close the cache version to free up its address space.
+ // 7) If we should relocate:
+ // a) If we have opened and checksum-verified the odex version relocate it to
+ // 'cache_filename' and return it
+ // b) If we have opened and checksum-verified the cache version relocate it in place and return
+ // it. This should not happen often (I think only the run-test's will hit this case).
+ // 8) If the cache-version was present we should delete it since it must be obsolete if we get to
+ // this point.
+ // 9) Return nullptr
+
+ *already_opened = false;
+ const Runtime* runtime = Runtime::Current();
+ CHECK(runtime != nullptr);
+ bool executable = !runtime->IsCompiler();
+
+ std::string odex_error_msg;
+ bool should_patch_system = false;
+ bool odex_checksum_verified = false;
+ {
+ // There is a high probability that these both these oat files map similar/the same address
+ // spaces so we must scope them like this so they each gets its turn.
+ std::unique_ptr<OatFile> odex_oat_file(OatFile::Open(odex_filename, odex_filename, NULL,
+ executable, &odex_error_msg));
+ if (odex_oat_file.get() != nullptr && CheckOatFile(odex_oat_file.get(), isa,
+ &odex_checksum_verified,
+ &odex_error_msg)) {
+ error_msgs->push_back(odex_error_msg);
+ return odex_oat_file.release();
+ } else if (odex_checksum_verified) {
+ // We can just relocate
+ should_patch_system = true;
+ odex_error_msg = "Image Patches are incorrect";
+ }
+ }
+
+
+ std::string cache_error_msg;
+ bool should_patch_cache = false;
+ bool cache_checksum_verified = false;
+ if (have_dalvik_cache) {
+ std::unique_ptr<OatFile> cache_oat_file(OatFile::Open(cache_filename, cache_filename, NULL,
+ executable, &cache_error_msg));
+ if (cache_oat_file.get() != nullptr && CheckOatFile(cache_oat_file.get(), isa,
+ &cache_checksum_verified,
+ &cache_error_msg)) {
+ error_msgs->push_back(cache_error_msg);
+ return cache_oat_file.release();
+ } else if (cache_checksum_verified) {
+ // We can just relocate
+ should_patch_cache = true;
+ cache_error_msg = "Image Patches are incorrect";
+ }
+ } else if (have_android_data) {
+ // dalvik_cache does not exist but android data does. This means we should be able to create
+ // it, so we should try.
+ GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA), true);
+ }
+
+ ret = nullptr;
+ std::string error_msg;
+ if (runtime->CanRelocate()) {
+ // Run relocation
+ const std::string& image_location =
+ Runtime::Current()->GetHeap()->GetImageSpace()->GetImageLocation();
+ if (odex_checksum_verified && should_patch_system) {
+ ret = PatchAndRetrieveOat(odex_filename, cache_filename, image_location, isa, &error_msg);
+ } else if (cache_checksum_verified && should_patch_cache) {
+ CHECK(have_dalvik_cache);
+ ret = PatchAndRetrieveOat(cache_filename, cache_filename, image_location, isa, &error_msg);
+ }
+ }
+ if (ret == nullptr && have_dalvik_cache && OS::FileExists(cache_filename.c_str())) {
+ // implicitly: were able to fine where the cached version is but we were unable to use it,
+ // either as a destination for relocation or to open a file. We should delete it if it is
+ // there.
+ if (TEMP_FAILURE_RETRY(unlink(cache_filename.c_str())) != 0) {
+ std::string rm_error_msg = StringPrintf("Failed to remove obsolete file from %s when "
+ "searching for dex file %s: %s",
+ cache_filename.c_str(), dex_location.c_str(),
+ strerror(errno));
+ error_msgs->push_back(rm_error_msg);
+ VLOG(class_linker) << rm_error_msg;
+ // Let the caller know that we couldn't remove the obsolete file.
+ // This is a good indication that further writes may fail as well.
+ *obsolete_file_cleanup_failed = true;
+ }
+ }
+ if (ret == nullptr) {
+ VLOG(class_linker) << error_msg;
+ error_msgs->push_back(error_msg);
+ std::string relocation_msg;
+ if (runtime->CanRelocate()) {
+ relocation_msg = StringPrintf(" and relocation failed");
+ }
+ if (have_dalvik_cache && cache_checksum_verified) {
+ error_msg = StringPrintf("Failed to open oat file from %s (error %s) or %s "
+ "(error %s)%s.", odex_filename.c_str(), odex_error_msg.c_str(),
+ cache_filename.c_str(), cache_error_msg.c_str(),
+ relocation_msg.c_str());
+ } else {
+ error_msg = StringPrintf("Failed to open oat file from %s (error %s) (no "
+ "dalvik_cache availible)%s.", odex_filename.c_str(),
+ odex_error_msg.c_str(), relocation_msg.c_str());
+ }
+ VLOG(class_linker) << error_msg;
+ error_msgs->push_back(error_msg);
+ }
+ return ret;
+}
+
+const OatFile* ClassLinker::PatchAndRetrieveOat(const std::string& input_oat,
+ const std::string& output_oat,
+ const std::string& image_location,
+ InstructionSet isa,
+ std::string* error_msg) {
+ Locks::mutator_lock_->AssertNotHeld(Thread::Current()); // Avoid starving GC.
+ std::string patchoat(Runtime::Current()->GetPatchoatExecutable());
+
+ std::string isa_arg("--instruction-set=");
+ isa_arg += GetInstructionSetString(isa);
+ std::string input_oat_filename_arg("--input-oat-file=");
+ input_oat_filename_arg += input_oat;
+ std::string output_oat_filename_arg("--output-oat-file=");
+ output_oat_filename_arg += output_oat;
+ std::string patched_image_arg("--patched-image-location=");
+ patched_image_arg += image_location;
+
+ std::vector<std::string> argv;
+ argv.push_back(patchoat);
+ argv.push_back(isa_arg);
+ argv.push_back(input_oat_filename_arg);
+ argv.push_back(output_oat_filename_arg);
+ argv.push_back(patched_image_arg);
+
+ std::string command_line(Join(argv, ' '));
+ LOG(INFO) << "Relocate Oat File: " << command_line;
+ bool success = Exec(argv, error_msg);
+ if (success) {
+ std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, NULL,
+ !Runtime::Current()->IsCompiler(), error_msg));
+ bool checksum_verified = false;
+ if (output.get() != nullptr && CheckOatFile(output.get(), isa, &checksum_verified, error_msg)) {
+ return output.release();
+ } else if (output.get() != nullptr) {
+ *error_msg = StringPrintf("Patching of oat file '%s' succeeded "
+ "but output file '%s' failed verifcation: %s",
+ input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
+ } else {
+ *error_msg = StringPrintf("Patching of oat file '%s' succeeded "
+ "but was unable to open output file '%s': %s",
+ input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
+ }
+ } else {
+ *error_msg = StringPrintf("Patching of oat file '%s to '%s' "
+ "failed: %s", input_oat.c_str(), output_oat.c_str(),
+ error_msg->c_str());
+ }
+ return nullptr;
+}
+
+int32_t ClassLinker::GetRequiredDelta(const OatFile* oat_file, InstructionSet isa) {
+ Runtime* runtime = Runtime::Current();
+ int32_t real_patch_delta;
+ const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+ if (isa == Runtime::Current()->GetInstructionSet()) {
+ const ImageHeader& image_header = image_space->GetImageHeader();
+ real_patch_delta = image_header.GetPatchDelta();
+ } else {
+ std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
+ image_space->GetImageLocation().c_str(), isa));
+ real_patch_delta = image_header->GetPatchDelta();
+ }
+ const OatHeader& oat_header = oat_file->GetOatHeader();
+ return real_patch_delta - oat_header.GetImagePatchDelta();
+}
+
+bool ClassLinker::CheckOatFile(const OatFile* oat_file, InstructionSet isa,
+ bool* checksum_verified,
+ std::string* error_msg) {
+ std::string compound_msg("Oat file failed to verify: ");
+ Runtime* runtime = Runtime::Current();
+ uint32_t real_image_checksum;
+ void* real_image_oat_offset;
+ int32_t real_patch_delta;
+ const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+ if (isa == Runtime::Current()->GetInstructionSet()) {
+ const ImageHeader& image_header = image_space->GetImageHeader();
+ real_image_checksum = image_header.GetOatChecksum();
+ real_image_oat_offset = image_header.GetOatDataBegin();
+ real_patch_delta = image_header.GetPatchDelta();
+ } else {
+ std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
+ image_space->GetImageLocation().c_str(), isa));
+ real_image_checksum = image_header->GetOatChecksum();
+ real_image_oat_offset = image_header->GetOatDataBegin();
+ real_patch_delta = image_header->GetPatchDelta();
+ }
+
+ const OatHeader& oat_header = oat_file->GetOatHeader();
+
+ uint32_t oat_image_checksum = oat_header.GetImageFileLocationOatChecksum();
+ *checksum_verified = oat_image_checksum == real_image_checksum;
+ if (!*checksum_verified) {
+ compound_msg += StringPrintf(" Oat Image Checksum Incorrect (expected 0x%x, recieved 0x%x)",
+ real_image_checksum, oat_image_checksum);
+ }
+
+ void* oat_image_oat_offset =
+ reinterpret_cast<void*>(oat_header.GetImageFileLocationOatDataBegin());
+ bool offset_verified = oat_image_oat_offset == real_image_oat_offset;
+ if (!offset_verified) {
+ compound_msg += StringPrintf(" Oat Image oat offset incorrect (expected 0x%p, recieved 0x%p)",
+ real_image_oat_offset, oat_image_oat_offset);
+ }
+
+ int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
+ bool patch_delta_verified = oat_patch_delta == real_patch_delta;
+ if (!patch_delta_verified) {
+ compound_msg += StringPrintf(" Oat image patch delta incorrect (expected 0x%x, recieved 0x%x)",
+ real_patch_delta, oat_patch_delta);
+ }
+
+ bool ret = (*checksum_verified && offset_verified && patch_delta_verified);
+ if (ret) {
+ *error_msg = compound_msg;
+ }
+ return ret;
+}
+
const OatFile* ClassLinker::FindOatFileFromOatLocation(const std::string& oat_location,
std::string* error_msg) {
const OatFile* oat_file = FindOpenedOatFileFromOatLocation(oat_location);
@@ -1668,7 +1915,9 @@
if (self->IsExceptionPending()) {
// An exception occured during load, set status to erroneous while holding klass' lock in case
// notification is necessary.
- klass->SetStatus(mirror::Class::kStatusError, self);
+ if (!klass->IsErroneous()) {
+ klass->SetStatus(mirror::Class::kStatusError, self);
+ }
return nullptr;
}
klass->SetClinitThreadId(self->GetTid());
@@ -1685,7 +1934,9 @@
CHECK(!klass->IsLoaded());
if (!LoadSuperAndInterfaces(klass, dex_file)) {
// Loading failed.
- klass->SetStatus(mirror::Class::kStatusError, self);
+ if (!klass->IsErroneous()) {
+ klass->SetStatus(mirror::Class::kStatusError, self);
+ }
return nullptr;
}
CHECK(klass->IsLoaded());
@@ -1697,7 +1948,9 @@
mirror::Class* new_class = nullptr;
if (!LinkClass(self, descriptor, klass, interfaces, &new_class)) {
// Linking failed.
- klass->SetStatus(mirror::Class::kStatusError, self);
+ if (!klass->IsErroneous()) {
+ klass->SetStatus(mirror::Class::kStatusError, self);
+ }
return nullptr;
}
CHECK(new_class != nullptr) << descriptor;
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index c17f88d..b108f61 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -265,10 +265,6 @@
std::string* error_msg)
LOCKS_EXCLUDED(Locks::mutator_lock_);
- const OatFile* FindOatFileFromOatLocation(const std::string& location,
- std::string* error_msg)
- LOCKS_EXCLUDED(dex_lock_);
-
// Find or create the oat file holding dex_location. Then load all corresponding dex files
// (if multidex) into the given vector.
bool OpenDexFilesFromOat(const char* dex_location, const char* oat_location,
@@ -546,9 +542,31 @@
const OatFile* FindOpenedOatFile(const char* oat_location, const char* dex_location,
const uint32_t* const dex_location_checksum)
LOCKS_EXCLUDED(dex_lock_);
+
+ // Will open the oat file directly without relocating, even if we could/should do relocation.
+ const OatFile* FindOatFileFromOatLocation(const std::string& oat_location,
+ std::string* error_msg)
+ LOCKS_EXCLUDED(dex_lock_);
+
const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location)
LOCKS_EXCLUDED(dex_lock_);
+ const OatFile* OpenOatFileFromDexLocation(const std::string& dex_location,
+ InstructionSet isa,
+ bool* already_opened,
+ bool* obsolete_file_cleanup_failed,
+ std::vector<std::string>* error_msg)
+ LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
+
+ const OatFile* PatchAndRetrieveOat(const std::string& input, const std::string& output,
+ const std::string& image_location, InstructionSet isa,
+ std::string* error_msg)
+ LOCKS_EXCLUDED(Locks::mutator_lock_);
+
+ bool CheckOatFile(const OatFile* oat_file, InstructionSet isa,
+ bool* checksum_verified, std::string* error_msg);
+ int32_t GetRequiredDelta(const OatFile* oat_file, InstructionSet isa);
+
// Note: will not register the oat file.
const OatFile* FindOatFileInOatLocationForDexFile(const char* dex_location,
uint32_t dex_location_checksum,
@@ -575,14 +593,10 @@
bool* obsolete_file_cleanup_failed)
LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
- // Find a verify an oat file with the given dex file. Will return nullptr when the oat file
- // was not found or the dex file could not be verified.
- // Note: Does not register the oat file.
- const OatFile* LoadOatFileAndVerifyDexFile(const std::string& oat_file_location,
- const char* dex_location,
- std::string* error_msg,
- bool* open_failed)
- LOCKS_EXCLUDED(dex_lock_);
+ // verify an oat file with the given dex file. Will return false when the dex file could not be
+ // verified. Will return true otherwise.
+ bool VerifyOatWithDexFile(const OatFile* oat_file, const char* dex_location,
+ std::string* error_msg);
mirror::ArtMethod* CreateProxyConstructor(Thread* self, Handle<mirror::Class> klass,
mirror::Class* proxy_class)
@@ -720,6 +734,8 @@
const void* quick_to_interpreter_bridge_trampoline_;
friend class ImageWriter; // for GetClassRoots
+ friend class ImageDumper; // for FindOpenedOatFileFromOatLocation
+ friend class ElfPatcher; // for FindOpenedOatFileForDexFile & FindOpenedOatFileFromOatLocation
FRIEND_TEST(ClassLinkerTest, ClassRootDescriptors);
FRIEND_TEST(mirror::DexCacheTest, Open);
FRIEND_TEST(ExceptionTest, FindExceptionHandler);
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index f47f13d..2826f89 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -199,12 +199,13 @@
runtime_->GetHeap()->VerifyHeap(); // Check for heap corruption before the test
}
-void CommonRuntimeTest::TearDown() {
- const char* android_data = getenv("ANDROID_DATA");
- ASSERT_TRUE(android_data != nullptr);
- DIR* dir = opendir(dalvik_cache_.c_str());
+
+void CommonRuntimeTest::ClearDirectory(const char* dirpath) {
+ ASSERT_TRUE(dirpath != nullptr);
+ DIR* dir = opendir(dirpath);
ASSERT_TRUE(dir != nullptr);
dirent* e;
+ struct stat s;
while ((e = readdir(dir)) != nullptr) {
if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) {
continue;
@@ -212,10 +213,24 @@
std::string filename(dalvik_cache_);
filename.push_back('/');
filename.append(e->d_name);
- int unlink_result = unlink(filename.c_str());
- ASSERT_EQ(0, unlink_result);
+ int stat_result = lstat(filename.c_str(), &s);
+ ASSERT_EQ(0, stat_result) << "unable to stat " << filename;
+ if (S_ISDIR(s.st_mode)) {
+ ClearDirectory(filename.c_str());
+ int rmdir_result = rmdir(filename.c_str());
+ ASSERT_EQ(0, rmdir_result) << filename;
+ } else {
+ int unlink_result = unlink(filename.c_str());
+ ASSERT_EQ(0, unlink_result) << filename;
+ }
}
closedir(dir);
+}
+
+void CommonRuntimeTest::TearDown() {
+ const char* android_data = getenv("ANDROID_DATA");
+ ASSERT_TRUE(android_data != nullptr);
+ ClearDirectory(dalvik_cache_.c_str());
int rmdir_cache_result = rmdir(dalvik_cache_.c_str());
ASSERT_EQ(0, rmdir_cache_result);
int rmdir_data_result = rmdir(android_data_.c_str());
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index d045031..eb96352 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -81,6 +81,8 @@
// Allow subclases such as CommonCompilerTest to add extra options.
virtual void SetUpRuntimeOptions(RuntimeOptions* options) {}
+ void ClearDirectory(const char* dirpath);
+
virtual void TearDown();
std::string GetLibCoreDexFileName();
diff --git a/runtime/compiler_callbacks.h b/runtime/compiler_callbacks.h
index b07043f..d1a6861 100644
--- a/runtime/compiler_callbacks.h
+++ b/runtime/compiler_callbacks.h
@@ -36,6 +36,10 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
virtual void ClassRejected(ClassReference ref) = 0;
+ // Return true if we should attempt to relocate to a random base address if we have not already
+ // done so. Return false if relocating in this way would be problematic.
+ virtual bool IsRelocationPossible() = 0;
+
protected:
CompilerCallbacks() { }
};
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 0fa0e41..c1c7631 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -110,8 +110,8 @@
void ThrowStackOverflowError(Thread* self) {
if (self->IsHandlingStackOverflow()) {
- LOG(ERROR) << "Recursive stack overflow.";
- // We don't fail here because SetStackEndForStackOverflow will print better diagnostics.
+ LOG(ERROR) << "Recursive stack overflow.";
+ // We don't fail here because SetStackEndForStackOverflow will print better diagnostics.
}
if (Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
@@ -123,15 +123,90 @@
JNIEnvExt* env = self->GetJniEnv();
std::string msg("stack size ");
msg += PrettySize(self->GetStackSize());
- // Use low-level JNI routine and pre-baked error class to avoid class linking operations that
- // would consume more stack.
- int rc = ::art::ThrowNewException(env, WellKnownClasses::java_lang_StackOverflowError,
- msg.c_str(), NULL);
- if (rc != JNI_OK) {
- // TODO: ThrowNewException failed presumably because of an OOME, we continue to throw the OOME
- // or die in the CHECK below. We may want to throw a pre-baked StackOverflowError
- // instead.
- LOG(ERROR) << "Couldn't throw new StackOverflowError because JNI ThrowNew failed.";
+
+ // Avoid running Java code for exception initialization.
+ // TODO: Checks to make this a bit less brittle.
+
+ std::string error_msg;
+
+ // Allocate an uninitialized object.
+ ScopedLocalRef<jobject> exc(env,
+ env->AllocObject(WellKnownClasses::java_lang_StackOverflowError));
+ if (exc.get() != nullptr) {
+ // "Initialize".
+ // StackOverflowError -> VirtualMachineError -> Error -> Throwable -> Object.
+ // Only Throwable has "custom" fields:
+ // String detailMessage.
+ // Throwable cause (= this).
+ // List<Throwable> suppressedExceptions (= Collections.emptyList()).
+ // Object stackState;
+ // StackTraceElement[] stackTrace;
+ // Only Throwable has a non-empty constructor:
+ // this.stackTrace = EmptyArray.STACK_TRACE_ELEMENT;
+ // fillInStackTrace();
+
+ // detailMessage.
+ // TODO: Use String::FromModifiedUTF...?
+ ScopedLocalRef<jstring> s(env, env->NewStringUTF(msg.c_str()));
+ if (s.get() != nullptr) {
+ jfieldID detail_message_id = env->GetFieldID(WellKnownClasses::java_lang_Throwable,
+ "detailMessage", "Ljava/lang/String;");
+ env->SetObjectField(exc.get(), detail_message_id, s.get());
+
+ // cause.
+ jfieldID cause_id = env->GetFieldID(WellKnownClasses::java_lang_Throwable,
+ "cause", "Ljava/lang/Throwable;");
+ env->SetObjectField(exc.get(), cause_id, exc.get());
+
+ // suppressedExceptions.
+ jfieldID emptylist_id = env->GetStaticFieldID(WellKnownClasses::java_util_Collections,
+ "EMPTY_LIST", "Ljava/util/List;");
+ ScopedLocalRef<jobject> emptylist(env, env->GetStaticObjectField(
+ WellKnownClasses::java_util_Collections, emptylist_id));
+ CHECK(emptylist.get() != nullptr);
+ jfieldID suppressed_id = env->GetFieldID(WellKnownClasses::java_lang_Throwable,
+ "suppressedExceptions", "Ljava/util/List;");
+ env->SetObjectField(exc.get(), suppressed_id, emptylist.get());
+
+ // stackState is set as result of fillInStackTrace. fillInStackTrace calls
+ // nativeFillInStackTrace.
+ ScopedLocalRef<jobject> stack_state_val(env, nullptr);
+ {
+ ScopedObjectAccessUnchecked soa(env);
+ stack_state_val.reset(soa.Self()->CreateInternalStackTrace<false>(soa));
+ }
+ if (stack_state_val.get() != nullptr) {
+ jfieldID stackstateID = env->GetFieldID(WellKnownClasses::java_lang_Throwable,
+ "stackState", "Ljava/lang/Object;");
+ env->SetObjectField(exc.get(), stackstateID, stack_state_val.get());
+
+ // stackTrace.
+ jfieldID stack_trace_elem_id = env->GetStaticFieldID(
+ WellKnownClasses::libcore_util_EmptyArray, "STACK_TRACE_ELEMENT",
+ "[Ljava/lang/StackTraceElement;");
+ ScopedLocalRef<jobject> stack_trace_elem(env, env->GetStaticObjectField(
+ WellKnownClasses::libcore_util_EmptyArray, stack_trace_elem_id));
+ jfieldID stacktrace_id = env->GetFieldID(
+ WellKnownClasses::java_lang_Throwable, "stackTrace", "[Ljava/lang/StackTraceElement;");
+ env->SetObjectField(exc.get(), stacktrace_id, stack_trace_elem.get());
+
+ // Throw the exception.
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ self->SetException(throw_location,
+ reinterpret_cast<mirror::Throwable*>(self->DecodeJObject(exc.get())));
+ } else {
+ error_msg = "Could not create stack trace.";
+ }
+ } else {
+ // Could not allocate a string object.
+ error_msg = "Couldn't throw new StackOverflowError because JNI NewStringUTF failed.";
+ }
+ } else {
+ error_msg = "Could not allocate StackOverflowError object.";
+ }
+
+ if (!error_msg.empty()) {
+ LOG(ERROR) << error_msg;
CHECK(self->IsExceptionPending());
}
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index a87aa89..7a3cbf3 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -16,6 +16,8 @@
#include "image_space.h"
+#include <random>
+
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "base/scoped_flock.h"
@@ -94,86 +96,304 @@
bool ImageSpace::FindImageFilename(const char* image_location,
const InstructionSet image_isa,
- std::string* image_filename,
- bool *is_system) {
+ std::string* system_filename,
+ bool* has_system,
+ std::string* cache_filename,
+ bool* dalvik_cache_exists,
+ bool* has_cache) {
+ *has_system = false;
+ *has_cache = false;
// image_location = /system/framework/boot.art
// system_image_location = /system/framework/<image_isa>/boot.art
std::string system_image_filename(GetSystemImageFilename(image_location, image_isa));
if (OS::FileExists(system_image_filename.c_str())) {
- *image_filename = system_image_filename;
- *is_system = true;
- return true;
+ *system_filename = system_image_filename;
+ *has_system = true;
}
- const std::string dalvik_cache = GetDalvikCacheOrDie(GetInstructionSetString(image_isa));
+ bool have_android_data = false;
+ *dalvik_cache_exists = false;
+ std::string dalvik_cache;
+ GetDalvikCache(GetInstructionSetString(image_isa), true, &dalvik_cache,
+ &have_android_data, dalvik_cache_exists);
- // Always set output location even if it does not exist,
- // so that the caller knows where to create the image.
- //
- // image_location = /system/framework/boot.art
- // *image_filename = /data/dalvik-cache/<image_isa>/boot.art
- *image_filename = GetDalvikCacheFilenameOrDie(image_location, dalvik_cache.c_str());
- *is_system = false;
- return OS::FileExists(image_filename->c_str());
+ if (have_android_data && *dalvik_cache_exists) {
+ // Always set output location even if it does not exist,
+ // so that the caller knows where to create the image.
+ //
+ // image_location = /system/framework/boot.art
+ // *image_filename = /data/dalvik-cache/<image_isa>/boot.art
+ std::string error_msg;
+ if (!GetDalvikCacheFilename(image_location, dalvik_cache.c_str(), cache_filename, &error_msg)) {
+ LOG(WARNING) << error_msg;
+ return *has_system;
+ }
+ *has_cache = OS::FileExists(cache_filename->c_str());
+ }
+ return *has_system || *has_cache;
+}
+
+static bool ReadSpecificImageHeader(const char* filename, ImageHeader* image_header) {
+ std::unique_ptr<File> image_file(OS::OpenFileForReading(filename));
+ if (image_file.get() == nullptr) {
+ return false;
+ }
+ const bool success = image_file->ReadFully(image_header, sizeof(ImageHeader));
+ if (!success || !image_header->IsValid()) {
+ return false;
+ }
+ return true;
+}
+
+static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) {
+ CHECK_ALIGNED(min_delta, kPageSize);
+ CHECK_ALIGNED(max_delta, kPageSize);
+ CHECK_LT(min_delta, max_delta);
+
+ std::default_random_engine generator;
+ generator.seed(NanoTime() * getpid());
+ std::uniform_int_distribution<int32_t> distribution(min_delta, max_delta);
+ int32_t r = distribution(generator);
+ if (r % 2 == 0) {
+ r = RoundUp(r, kPageSize);
+ } else {
+ r = RoundDown(r, kPageSize);
+ }
+ CHECK_LE(min_delta, r);
+ CHECK_GE(max_delta, r);
+ CHECK_ALIGNED(r, kPageSize);
+ return r;
+}
+
+bool ImageSpace::RelocateImage(const char* image_location, const char* dest_filename,
+ InstructionSet isa, std::string* error_msg) {
+ std::string patchoat(Runtime::Current()->GetPatchoatExecutable());
+
+ std::string input_image_location_arg("--input-image-location=");
+ input_image_location_arg += image_location;
+
+ std::string output_image_filename_arg("--output-image-file=");
+ output_image_filename_arg += dest_filename;
+
+ std::string input_oat_location_arg("--input-oat-location=");
+ input_oat_location_arg += ImageHeader::GetOatLocationFromImageLocation(image_location);
+
+ std::string output_oat_filename_arg("--output-oat-file=");
+ output_oat_filename_arg += ImageHeader::GetOatLocationFromImageLocation(dest_filename);
+
+ std::string instruction_set_arg("--instruction-set=");
+ instruction_set_arg += GetInstructionSetString(isa);
+
+ std::string base_offset_arg("--base-offset-delta=");
+ StringAppendF(&base_offset_arg, "%d", ChooseRelocationOffsetDelta(ART_BASE_ADDRESS_MIN_DELTA,
+ ART_BASE_ADDRESS_MAX_DELTA));
+
+ std::vector<std::string> argv;
+ argv.push_back(patchoat);
+
+ argv.push_back(input_image_location_arg);
+ argv.push_back(output_image_filename_arg);
+
+ argv.push_back(input_oat_location_arg);
+ argv.push_back(output_oat_filename_arg);
+
+ argv.push_back(instruction_set_arg);
+ argv.push_back(base_offset_arg);
+
+ std::string command_line(Join(argv, ' '));
+ LOG(INFO) << "RelocateImage: " << command_line;
+ return Exec(argv, error_msg);
+}
+
+static ImageHeader* ReadSpecificImageHeaderOrDie(const char* filename) {
+ std::unique_ptr<ImageHeader> hdr(new ImageHeader);
+ if (!ReadSpecificImageHeader(filename, hdr.get())) {
+ LOG(FATAL) << "Unable to read image header for " << filename;
+ return nullptr;
+ }
+ return hdr.release();
}
ImageHeader* ImageSpace::ReadImageHeaderOrDie(const char* image_location,
const InstructionSet image_isa) {
- std::string image_filename;
- bool is_system = false;
- if (FindImageFilename(image_location, image_isa, &image_filename, &is_system)) {
- std::unique_ptr<File> image_file(OS::OpenFileForReading(image_filename.c_str()));
- std::unique_ptr<ImageHeader> image_header(new ImageHeader);
- const bool success = image_file->ReadFully(image_header.get(), sizeof(ImageHeader));
- if (!success || !image_header->IsValid()) {
- LOG(FATAL) << "Invalid Image header for: " << image_filename;
- return nullptr;
+ std::string system_filename;
+ bool has_system = false;
+ std::string cache_filename;
+ bool has_cache = false;
+ bool dalvik_cache_exists = false;
+ if (FindImageFilename(image_location, image_isa, &system_filename, &has_system,
+ &cache_filename, &dalvik_cache_exists, &has_cache)) {
+ if (Runtime::Current()->ShouldRelocate()) {
+ if (has_system && has_cache) {
+ std::unique_ptr<ImageHeader> sys_hdr(new ImageHeader);
+ std::unique_ptr<ImageHeader> cache_hdr(new ImageHeader);
+ if (!ReadSpecificImageHeader(system_filename.c_str(), sys_hdr.get())) {
+ LOG(FATAL) << "Unable to read image header for " << image_location << " at "
+ << system_filename;
+ return nullptr;
+ }
+ if (!ReadSpecificImageHeader(cache_filename.c_str(), cache_hdr.get())) {
+ LOG(FATAL) << "Unable to read image header for " << image_location << " at "
+ << cache_filename;
+ return nullptr;
+ }
+ if (sys_hdr->GetOatChecksum() != cache_hdr->GetOatChecksum()) {
+ LOG(FATAL) << "Unable to find a relocated version of image file " << image_location;
+ return nullptr;
+ }
+ return cache_hdr.release();
+ } else if (!has_cache) {
+ LOG(FATAL) << "Unable to find a relocated version of image file " << image_location;
+ return nullptr;
+ } else if (!has_system && has_cache) {
+ // This can probably just use the cache one.
+ return ReadSpecificImageHeaderOrDie(cache_filename.c_str());
+ }
+ } else {
+ // We don't want to relocate, Just pick the appropriate one if we have it and return.
+ if (has_system && has_cache) {
+ // We want the cache if the checksum matches, otherwise the system.
+ std::unique_ptr<ImageHeader> system(ReadSpecificImageHeaderOrDie(system_filename.c_str()));
+ std::unique_ptr<ImageHeader> cache(ReadSpecificImageHeaderOrDie(cache_filename.c_str()));
+ if (system.get() == nullptr ||
+ (cache.get() != nullptr && cache->GetOatChecksum() == system->GetOatChecksum())) {
+ return cache.release();
+ } else {
+ return system.release();
+ }
+ } else if (has_system) {
+ return ReadSpecificImageHeaderOrDie(system_filename.c_str());
+ } else if (has_cache) {
+ return ReadSpecificImageHeaderOrDie(cache_filename.c_str());
+ }
}
-
- return image_header.release();
}
LOG(FATAL) << "Unable to find image file for: " << image_location;
return nullptr;
}
+static bool ChecksumsMatch(const char* image_a, const char* image_b) {
+ ImageHeader hdr_a;
+ ImageHeader hdr_b;
+ return ReadSpecificImageHeader(image_a, &hdr_a) && ReadSpecificImageHeader(image_b, &hdr_b)
+ && hdr_a.GetOatChecksum() == hdr_b.GetOatChecksum();
+}
+
ImageSpace* ImageSpace::Create(const char* image_location,
const InstructionSet image_isa) {
- std::string image_filename;
std::string error_msg;
- bool is_system = false;
- const bool found_image = FindImageFilename(image_location, image_isa, &image_filename,
- &is_system);
+ std::string system_filename;
+ bool has_system = false;
+ std::string cache_filename;
+ bool has_cache = false;
+ bool dalvik_cache_exists = false;
+ const bool found_image = FindImageFilename(image_location, image_isa, &system_filename,
+ &has_system, &cache_filename, &dalvik_cache_exists,
+ &has_cache);
- // Note that we must not use the file descriptor associated with
- // ScopedFlock::GetFile to Init the image file. We want the file
- // descriptor (and the associated exclusive lock) to be released when
- // we leave Create.
- ScopedFlock image_lock;
- image_lock.Init(image_filename.c_str(), &error_msg);
-
+ ImageSpace* space;
+ bool relocate = Runtime::Current()->ShouldRelocate();
if (found_image) {
- ImageSpace* space = ImageSpace::Init(image_filename.c_str(), image_location, !is_system,
- &error_msg);
+ const std::string* image_filename;
+ bool is_system = false;
+ bool relocated_version_used = false;
+ if (relocate) {
+ CHECK(dalvik_cache_exists) << "Requiring relocation for image " << image_location << " "
+ << "at " << system_filename << " but we do not have any "
+ << "dalvik_cache to find/place it in.";
+ if (has_system) {
+ if (has_cache && ChecksumsMatch(system_filename.c_str(), cache_filename.c_str())) {
+ // We already have a relocated version
+ image_filename = &cache_filename;
+ relocated_version_used = true;
+ } else {
+ // We cannot have a relocated version, Relocate the system one and use it.
+ if (RelocateImage(image_location, cache_filename.c_str(), image_isa,
+ &error_msg)) {
+ relocated_version_used = true;
+ image_filename = &cache_filename;
+ } else {
+ LOG(FATAL) << "Unable to relocate image " << image_location << " "
+ << "from " << system_filename << " to " << cache_filename << ": "
+ << error_msg;
+ return nullptr;
+ }
+ }
+ } else {
+ CHECK(has_cache);
+ // We can just use cache's since it should be fine. This might or might not be relocated.
+ image_filename = &cache_filename;
+ }
+ } else {
+ if (has_system && has_cache) {
+ // Check they have the same cksum. If they do use the cache. Otherwise system.
+ if (ChecksumsMatch(system_filename.c_str(), cache_filename.c_str())) {
+ image_filename = &cache_filename;
+ relocated_version_used = true;
+ } else {
+ image_filename = &system_filename;
+ }
+ } else if (has_system) {
+ image_filename = &system_filename;
+ } else {
+ CHECK(has_cache);
+ image_filename = &cache_filename;
+ }
+ }
+ {
+ // Note that we must not use the file descriptor associated with
+ // ScopedFlock::GetFile to Init the image file. We want the file
+ // descriptor (and the associated exclusive lock) to be released when
+ // we leave Create.
+ ScopedFlock image_lock;
+ image_lock.Init(image_filename->c_str(), &error_msg);
+ LOG(INFO) << "Using image file " << image_filename->c_str() << " for image location "
+ << image_location;
+ space = ImageSpace::Init(image_filename->c_str(), image_location,
+ false, &error_msg);
+ }
if (space != nullptr) {
return space;
}
- // If the /system file exists, it should be up-to-date, don't try to generate it.
- // If it's not the /system file, log a warning and fall through to GenerateImage.
- if (is_system) {
- LOG(FATAL) << "Failed to load image '" << image_filename << "': " << error_msg;
+ // If the /system file exists, it should be up-to-date, don't try to generate it. Same if it is
+ // a relocated copy from something in /system (i.e. checksum's match).
+ // Otherwise, log a warning and fall through to GenerateImage.
+ if (relocated_version_used) {
+ LOG(FATAL) << "Attempted to use relocated version of " << image_location << " "
+ << "at " << cache_filename << " generated from " << system_filename << " "
+ << "but image failed to load: " << error_msg;
+ return nullptr;
+ } else if (is_system) {
+ LOG(FATAL) << "Failed to load /system image '" << *image_filename << "': " << error_msg;
return nullptr;
} else {
LOG(WARNING) << error_msg;
}
}
- CHECK(GenerateImage(image_filename, &error_msg))
- << "Failed to generate image '" << image_filename << "': " << error_msg;
- ImageSpace* space = ImageSpace::Init(image_filename.c_str(), image_location, true, &error_msg);
+ CHECK(dalvik_cache_exists) << "No place to put generated image.";
+ CHECK(GenerateImage(cache_filename, &error_msg))
+ << "Failed to generate image '" << cache_filename << "': " << error_msg;
+ // TODO Should I relocate this image? Sure
+ if (relocate) {
+ if (!RelocateImage(cache_filename.c_str(), cache_filename.c_str(), image_isa, &error_msg)) {
+ LOG(FATAL) << "Failed to relocate newly created image " << cache_filename.c_str();
+ return nullptr;
+ }
+ }
+ {
+ // Note that we must not use the file descriptor associated with
+ // ScopedFlock::GetFile to Init the image file. We want the file
+ // descriptor (and the associated exclusive lock) to be released when
+ // we leave Create.
+ ScopedFlock image_lock;
+ image_lock.Init(cache_filename.c_str(), &error_msg);
+ space = ImageSpace::Init(cache_filename.c_str(), image_location, true, &error_msg);
+ }
if (space == nullptr) {
- LOG(FATAL) << "Failed to load image '" << image_filename << "': " << error_msg;
+ LOG(FATAL) << "Failed to load generated image '" << cache_filename << "': " << error_msg;
}
return space;
}
@@ -316,6 +536,15 @@
" in image %s", oat_checksum, image_oat_checksum, GetName());
return nullptr;
}
+ int32_t image_patch_delta = image_header.GetPatchDelta();
+ int32_t oat_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta();
+ if (oat_patch_delta != image_patch_delta) {
+ // We should have already relocated by this point. Bail out.
+ *error_msg = StringPrintf("Failed to match oat file patch delta %d to expected patch delta %d "
+ "in image %s", oat_patch_delta, image_patch_delta, GetName());
+ return nullptr;
+ }
+
return oat_file;
}
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index dd9b580..debca52 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -98,6 +98,20 @@
return false;
}
+ // Returns the filename of the image corresponding to
+ // requested image_location, or the filename where a new image
+ // should be written if one doesn't exist. Looks for a generated
+ // image in the specified location and then in the dalvik-cache.
+ //
+ // Returns true if an image was found, false otherwise.
+ static bool FindImageFilename(const char* image_location,
+ InstructionSet image_isa,
+ std::string* system_location,
+ bool* has_system,
+ std::string* data_location,
+ bool* dalvik_cache_exists,
+ bool* has_data);
+
private:
// Tries to initialize an ImageSpace from the given image path,
// returning NULL on error.
@@ -110,16 +124,8 @@
bool validate_oat_file, std::string* error_msg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Returns the filename of the image corresponding to
- // requested image_location, or the filename where a new image
- // should be written if one doesn't exist. Looks for a generated
- // image in the specified location and then in the dalvik-cache.
- //
- // Returns true if an image was found, false otherwise.
- static bool FindImageFilename(const char* image_location,
- InstructionSet image_isa,
- std::string* location,
- bool* is_system);
+ static bool RelocateImage(const char* image_location, const char* dest_filename,
+ InstructionSet isa, std::string* error_msg);
OatFile* OpenOatFile(const char* image, std::string* error_msg) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 8c0f3fb..fd67197 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -151,7 +151,8 @@
enum HprofHeapId {
HPROF_HEAP_DEFAULT = 0,
HPROF_HEAP_ZYGOTE = 'Z',
- HPROF_HEAP_APP = 'A'
+ HPROF_HEAP_APP = 'A',
+ HPROF_HEAP_IMAGE = 'I',
};
enum HprofBasicType {
@@ -846,26 +847,37 @@
int Hprof::DumpHeapObject(mirror::Object* obj) {
HprofRecord* rec = ¤t_record_;
- HprofHeapId desiredHeap = false ? HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP; // TODO: zygote objects?
-
+ gc::space::ContinuousSpace* space =
+ Runtime::Current()->GetHeap()->FindContinuousSpaceFromObject(obj, true);
+ HprofHeapId heap_type = HPROF_HEAP_APP;
+ if (space != nullptr) {
+ if (space->IsZygoteSpace()) {
+ heap_type = HPROF_HEAP_ZYGOTE;
+ } else if (space->IsImageSpace()) {
+ heap_type = HPROF_HEAP_IMAGE;
+ }
+ }
if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->Size() >= BYTES_PER_SEGMENT) {
StartNewHeapDumpSegment();
}
- if (desiredHeap != current_heap_) {
+ if (heap_type != current_heap_) {
HprofStringId nameId;
// This object is in a different heap than the current one.
// Emit a HEAP_DUMP_INFO tag to change heaps.
rec->AddU1(HPROF_HEAP_DUMP_INFO);
- rec->AddU4((uint32_t)desiredHeap); // uint32_t: heap id
- switch (desiredHeap) {
+ rec->AddU4(static_cast<uint32_t>(heap_type)); // uint32_t: heap type
+ switch (heap_type) {
case HPROF_HEAP_APP:
nameId = LookupStringId("app");
break;
case HPROF_HEAP_ZYGOTE:
nameId = LookupStringId("zygote");
break;
+ case HPROF_HEAP_IMAGE:
+ nameId = LookupStringId("image");
+ break;
default:
// Internal error
LOG(ERROR) << "Unexpected desiredHeap";
@@ -873,7 +885,7 @@
break;
}
rec->AddStringId(nameId);
- current_heap_ = desiredHeap;
+ current_heap_ = heap_type;
}
mirror::Class* c = obj->GetClass();
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index ad798ed..26ddba2 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -74,6 +74,7 @@
table_mem_map_.reset(MemMap::MapAnonymous("indirect ref table", nullptr, table_bytes,
PROT_READ | PROT_WRITE, false, &error_str));
CHECK(table_mem_map_.get() != nullptr) << error_str;
+ CHECK_EQ(table_mem_map_->Size(), table_bytes);
table_ = reinterpret_cast<mirror::Object**>(table_mem_map_->Begin());
CHECK(table_ != nullptr);
diff --git a/runtime/instruction_set.cc b/runtime/instruction_set.cc
index 5b60396..d7e358c 100644
--- a/runtime/instruction_set.cc
+++ b/runtime/instruction_set.cc
@@ -83,6 +83,44 @@
}
}
+
+static constexpr size_t kDefaultStackOverflowReservedBytes = 16 * KB;
+static constexpr size_t kMipsStackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
+
+// TODO: Lower once implicit stack-overflow checks can work with less than 16K.
+static constexpr size_t kArmStackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB;
+static constexpr size_t kArm64StackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB;
+static constexpr size_t kX86StackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB;
+static constexpr size_t kX86_64StackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB;
+
+size_t GetStackOverflowReservedBytes(InstructionSet isa) {
+ switch (isa) {
+ case kArm: // Intentional fall-through.
+ case kThumb2:
+ return kArmStackOverflowReservedBytes;
+
+ case kArm64:
+ return kArm64StackOverflowReservedBytes;
+
+ case kMips:
+ return kMipsStackOverflowReservedBytes;
+
+ case kX86:
+ return kX86StackOverflowReservedBytes;
+
+ case kX86_64:
+ return kX86_64StackOverflowReservedBytes;
+
+ case kNone:
+ LOG(FATAL) << "kNone has no stack overflow size";
+ return 0;
+
+ default:
+ LOG(FATAL) << "Unknown instruction set" << isa;
+ return 0;
+ }
+}
+
std::string InstructionSetFeatures::GetFeatureString() const {
std::string result;
if ((mask_ & kHwDiv) != 0) {
diff --git a/runtime/instruction_set.h b/runtime/instruction_set.h
index dce1c15..f212811 100644
--- a/runtime/instruction_set.h
+++ b/runtime/instruction_set.h
@@ -169,33 +169,7 @@
}
}
-static constexpr size_t kDefaultStackOverflowReservedBytes = 16 * KB;
-static constexpr size_t kArmStackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
-static constexpr size_t kMipsStackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
-
-// TODO: shrink reserved space, in particular for 64bit.
-
-// Worst-case, we would need about 2.6x the amount of x86_64 for many more registers.
-// But this one works rather well.
-static constexpr size_t kArm64StackOverflowReservedBytes = 32 * KB;
-// TODO: Bumped to workaround regression (http://b/14982147) Specifically to fix:
-// test-art-host-run-test-interpreter-018-stack-overflow
-// test-art-host-run-test-interpreter-107-int-math2
-static constexpr size_t kX86StackOverflowReservedBytes = (kIsDebugBuild ? 32 : 24) * KB;
-static constexpr size_t kX86_64StackOverflowReservedBytes = 32 * KB;
-
-static constexpr size_t GetStackOverflowReservedBytes(InstructionSet isa) {
- return (isa == kArm || isa == kThumb2) ? kArmStackOverflowReservedBytes :
- isa == kArm64 ? kArm64StackOverflowReservedBytes :
- isa == kMips ? kMipsStackOverflowReservedBytes :
- isa == kX86 ? kX86StackOverflowReservedBytes :
- isa == kX86_64 ? kX86_64StackOverflowReservedBytes :
- isa == kNone ? (LOG(FATAL) << "kNone has no stack overflow size", 0) :
- (LOG(FATAL) << "Unknown instruction set" << isa, 0);
-}
-
-static constexpr size_t kRuntimeStackOverflowReservedBytes =
- GetStackOverflowReservedBytes(kRuntimeISA);
+size_t GetStackOverflowReservedBytes(InstructionSet isa);
enum InstructionFeatures {
kHwDiv = 0x1, // Supports hardware divide.
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index e3068b3..47a7f0d 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -397,7 +397,8 @@
void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object* receiver,
uint32_t* args, JValue* result) {
DCHECK_EQ(self, Thread::Current());
- if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) {
+ bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks();
+ if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEndForInterpreter(implicit_check))) {
ThrowStackOverflowError(self);
return;
}
@@ -509,7 +510,8 @@
JValue EnterInterpreterFromStub(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame) {
DCHECK_EQ(self, Thread::Current());
- if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) {
+ bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks();
+ if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEndForInterpreter(implicit_check))) {
ThrowStackOverflowError(self);
return JValue();
}
@@ -520,7 +522,8 @@
extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh,
const DexFile::CodeItem* code_item,
ShadowFrame* shadow_frame, JValue* result) {
- if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) {
+ bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks();
+ if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEndForInterpreter(implicit_check))) {
ThrowStackOverflowError(self);
return;
}
diff --git a/runtime/noop_compiler_callbacks.h b/runtime/noop_compiler_callbacks.h
index 65498de..e9ad353 100644
--- a/runtime/noop_compiler_callbacks.h
+++ b/runtime/noop_compiler_callbacks.h
@@ -32,6 +32,11 @@
void ClassRejected(ClassReference ref) OVERRIDE {}
+ // This is only used by compilers which need to be able to run without relocation even when it
+ // would normally be enabled. For example the patchoat executable, and dex2oat --image, both need
+ // to disable the relocation since both deal with writing out the images directly.
+ bool IsRelocationPossible() OVERRIDE { return false; }
+
private:
DISALLOW_COPY_AND_ASSIGN(NoopCompilerCallbacks);
};
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 1421baf..0a8c35b 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -23,7 +23,7 @@
namespace art {
const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '3', '7', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '3', '8', '\0' };
static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
size_t estimate = 0U;
@@ -67,6 +67,8 @@
const SafeMap<std::string, std::string>* variable_data) {
memcpy(magic_, kOatMagic, sizeof(kOatMagic));
memcpy(version_, kOatVersion, sizeof(kOatVersion));
+ executable_offset_ = 0;
+ image_patch_delta_ = 0;
adler32_checksum_ = adler32(0L, Z_NULL, 0);
@@ -98,7 +100,6 @@
UpdateChecksum(&key_value_store_, key_value_store_size_);
}
- executable_offset_ = 0;
interpreter_to_interpreter_bridge_offset_ = 0;
interpreter_to_compiled_code_bridge_offset_ = 0;
jni_dlsym_lookup_offset_ = 0;
@@ -118,6 +119,12 @@
if (memcmp(version_, kOatVersion, sizeof(kOatVersion)) != 0) {
return false;
}
+ if (!IsAligned<kPageSize>(executable_offset_)) {
+ return false;
+ }
+ if (!IsAligned<kPageSize>(image_patch_delta_)) {
+ return false;
+ }
return true;
}
@@ -355,6 +362,26 @@
UpdateChecksum(&quick_to_interpreter_bridge_offset_, sizeof(offset));
}
+int32_t OatHeader::GetImagePatchDelta() const {
+ CHECK(IsValid());
+ return image_patch_delta_;
+}
+
+void OatHeader::RelocateOat(off_t delta) {
+ CHECK(IsValid());
+ CHECK_ALIGNED(delta, kPageSize);
+ image_patch_delta_ += delta;
+ if (image_file_location_oat_data_begin_ != 0) {
+ image_file_location_oat_data_begin_ += delta;
+ }
+}
+
+void OatHeader::SetImagePatchDelta(int32_t off) {
+ CHECK(IsValid());
+ CHECK_ALIGNED(off, kPageSize);
+ image_patch_delta_ = off;
+}
+
uint32_t OatHeader::GetImageFileLocationOatChecksum() const {
CHECK(IsValid());
return image_file_location_oat_checksum_;
diff --git a/runtime/oat.h b/runtime/oat.h
index fbed596..6d5fefe 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -88,6 +88,10 @@
uint32_t GetQuickToInterpreterBridgeOffset() const;
void SetQuickToInterpreterBridgeOffset(uint32_t offset);
+ int32_t GetImagePatchDelta() const;
+ void RelocateOat(off_t delta);
+ void SetImagePatchDelta(int32_t off);
+
InstructionSet GetInstructionSet() const;
const InstructionSetFeatures& GetInstructionSetFeatures() const;
uint32_t GetImageFileLocationOatChecksum() const;
@@ -129,6 +133,9 @@
uint32_t quick_resolution_trampoline_offset_;
uint32_t quick_to_interpreter_bridge_offset_;
+ // The amount that the image this oat is associated with has been patched.
+ int32_t image_patch_delta_;
+
uint32_t image_file_location_oat_checksum_;
uint32_t image_file_location_oat_data_begin_;
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 86c1bae..f9cc36a 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -87,6 +87,11 @@
return OpenElfFile(file, location, NULL, true, false, error_msg);
}
+OatFile* OatFile::OpenReadable(File* file, const std::string& location, std::string* error_msg) {
+ CheckLocation(location);
+ return OpenElfFile(file, location, NULL, false, false, error_msg);
+}
+
OatFile* OatFile::OpenDlopen(const std::string& elf_filename,
const std::string& location,
byte* requested_base,
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 44f4466..3ec2e84 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -52,6 +52,8 @@
// ImageWriter which wants to open a writable version from an existing
// file descriptor for patching.
static OatFile* OpenWritable(File* file, const std::string& location, std::string* error_msg);
+ // Opens an oat file from an already opened File. Maps it PROT_READ, MAP_PRIVATE.
+ static OatFile* OpenReadable(File* file, const std::string& location, std::string* error_msg);
// Open an oat file backed by a std::vector with the given location.
static OatFile* OpenMemory(std::vector<uint8_t>& oat_contents,
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 9a1d0f7..8d0aff8 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -221,6 +221,7 @@
compiler_callbacks_ = nullptr;
is_zygote_ = false;
+ must_relocate_ = kDefaultMustRelocate;
if (kPoisonHeapReferences) {
// kPoisonHeapReferences currently works only with the interpreter only.
// TODO: make it work with the compiler.
@@ -390,6 +391,7 @@
ignore_max_footprint_ = true;
} else if (option == "-XX:LowMemoryMode") {
low_memory_mode_ = true;
+ // TODO Might want to turn off must_relocate here.
} else if (option == "-XX:UseTLAB") {
use_tlab_ = true;
} else if (option == "-XX:EnableHSpaceCompactForOOM") {
@@ -408,6 +410,14 @@
reinterpret_cast<const char*>(options[i].second));
} else if (option == "-Xzygote") {
is_zygote_ = true;
+ } else if (StartsWith(option, "-Xpatchoat:")) {
+ if (!ParseStringAfterChar(option, ':', &patchoat_executable_)) {
+ return false;
+ }
+ } else if (option == "-Xrelocate") {
+ must_relocate_ = true;
+ } else if (option == "-Xnorelocate") {
+ must_relocate_ = false;
} else if (option == "-Xint") {
interpreter_only_ = true;
} else if (StartsWith(option, "-Xgc:")) {
@@ -758,6 +768,8 @@
UsageMessage(stream, " -Xcompiler:filename\n");
UsageMessage(stream, " -Xcompiler-option dex2oat-option\n");
UsageMessage(stream, " -Ximage-compiler-option dex2oat-option\n");
+ UsageMessage(stream, " -Xpatchoat:filename\n");
+ UsageMessage(stream, " -X[no]relocate\n");
UsageMessage(stream, "\n");
UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n");
diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h
index 23f2bcf..29d5494 100644
--- a/runtime/parsed_options.h
+++ b/runtime/parsed_options.h
@@ -47,6 +47,10 @@
std::string jni_trace_;
CompilerCallbacks* compiler_callbacks_;
bool is_zygote_;
+ // TODO Change this to true when we want it on by default.
+ static constexpr bool kDefaultMustRelocate = false;
+ bool must_relocate_;
+ std::string patchoat_executable_;
bool interpreter_only_;
bool is_explicit_gc_disabled_;
bool use_tlab_;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index aca2607..b7eae85 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -101,6 +101,7 @@
instruction_set_(kNone),
compiler_callbacks_(nullptr),
is_zygote_(false),
+ must_relocate_(false),
is_concurrent_gc_enabled_(true),
is_explicit_gc_disabled_(false),
default_stack_size_(0),
@@ -385,6 +386,15 @@
return env->NewGlobalRef(system_class_loader.get());
}
+std::string Runtime::GetPatchoatExecutable() const {
+ if (!patchoat_executable_.empty()) {
+ return patchoat_executable_;
+ }
+ std::string patchoat_executable_(GetAndroidRoot());
+ patchoat_executable_ += (kIsDebugBuild ? "/bin/patchoatd" : "/bin/patchoat");
+ return patchoat_executable_;
+}
+
std::string Runtime::GetCompilerExecutable() const {
if (!compiler_executable_.empty()) {
return compiler_executable_;
@@ -557,6 +567,8 @@
properties_ = options->properties_;
compiler_callbacks_ = options->compiler_callbacks_;
+ patchoat_executable_ = options->patchoat_executable_;
+ must_relocate_ = options->must_relocate_;
is_zygote_ = options->is_zygote_;
is_explicit_gc_disabled_ = options->is_explicit_gc_disabled_;
@@ -629,8 +641,7 @@
break;
}
- if (!options->interpreter_only_ &&
- (implicit_null_checks_ || implicit_so_checks_ || implicit_suspend_checks_)) {
+ if (implicit_null_checks_ || implicit_so_checks_ || implicit_suspend_checks_) {
fault_manager.Init();
// These need to be in a specific order. The null point check handler must be
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 6a5fe75..c81e36a 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -26,6 +26,7 @@
#include <utility>
#include <vector>
+#include "compiler_callbacks.h"
#include "instrumentation.h"
#include "instruction_set.h"
#include "jobject_comparator.h"
@@ -55,7 +56,6 @@
class MethodVerifier;
}
class ClassLinker;
-class CompilerCallbacks;
class DexFile;
class InternTable;
class JavaVMExt;
@@ -92,6 +92,18 @@
return compiler_callbacks_ != nullptr;
}
+ bool CanRelocate() const {
+ return !IsCompiler() || compiler_callbacks_->IsRelocationPossible();
+ }
+
+ bool ShouldRelocate() const {
+ return must_relocate_ && CanRelocate();
+ }
+
+ bool MustRelocateIfPossible() const {
+ return must_relocate_;
+ }
+
CompilerCallbacks* GetCompilerCallbacks() {
return compiler_callbacks_;
}
@@ -105,6 +117,7 @@
}
std::string GetCompilerExecutable() const;
+ std::string GetPatchoatExecutable() const;
const std::vector<std::string>& GetCompilerOptions() const {
return compiler_options_;
@@ -486,10 +499,12 @@
CompilerCallbacks* compiler_callbacks_;
bool is_zygote_;
+ bool must_relocate_;
bool is_concurrent_gc_enabled_;
bool is_explicit_gc_disabled_;
std::string compiler_executable_;
+ std::string patchoat_executable_;
std::vector<std::string> compiler_options_;
std::vector<std::string> image_compiler_options_;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index ba5f9d4..8151464 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -76,6 +76,8 @@
bool Thread::is_started_ = false;
pthread_key_t Thread::pthread_key_self_;
ConditionVariable* Thread::resume_cond_ = nullptr;
+const size_t Thread::kStackOverflowImplicitCheckSize = kStackOverflowProtectedSize +
+ GetStackOverflowReservedBytes(kRuntimeISA);
static const char* kThreadNameDuringStartup = "<native thread without managed peer>";
@@ -219,7 +221,7 @@
// It's likely that callers are trying to ensure they have at least a certain amount of
// stack space, so we should add our reserved space on top of what they requested, rather
// than implicitly take it away from them.
- stack_size += kRuntimeStackOverflowReservedBytes;
+ stack_size += GetStackOverflowReservedBytes(kRuntimeISA);
} else {
// If we are going to use implicit stack checks, allocate space for the protected
// region at the bottom of the stack.
@@ -308,7 +310,7 @@
if (mprotect(pregion, kStackOverflowProtectedSize, PROT_NONE) == -1) {
LOG(FATAL) << "Unable to create protected region in stack for implicit overflow check. Reason:"
- << strerror(errno);
+ << strerror(errno) << kStackOverflowProtectedSize;
}
// Tell the kernel that we won't be needing these pages any more.
@@ -536,7 +538,7 @@
tlsPtr_.stack_begin = reinterpret_cast<byte*>(read_stack_base);
tlsPtr_.stack_size = read_stack_size;
- if (read_stack_size <= kRuntimeStackOverflowReservedBytes) {
+ if (read_stack_size <= GetStackOverflowReservedBytes(kRuntimeISA)) {
LOG(FATAL) << "Attempt to attach a thread with a too-small stack (" << read_stack_size
<< " bytes)";
}
@@ -2247,7 +2249,7 @@
if (tlsPtr_.stack_end == tlsPtr_.stack_begin) {
// However, we seem to have already extended to use the full stack.
LOG(ERROR) << "Need to increase kStackOverflowReservedBytes (currently "
- << kRuntimeStackOverflowReservedBytes << ")?";
+ << GetStackOverflowReservedBytes(kRuntimeISA) << ")?";
DumpStack(LOG(ERROR));
LOG(FATAL) << "Recursive stack overflow.";
}
diff --git a/runtime/thread.h b/runtime/thread.h
index d08c2fc..998f7db 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -104,8 +104,7 @@
// is protected against reads and the lower is available for use while
// throwing the StackOverflow exception.
static constexpr size_t kStackOverflowProtectedSize = 16 * KB;
- static constexpr size_t kStackOverflowImplicitCheckSize = kStackOverflowProtectedSize +
- kRuntimeStackOverflowReservedBytes;
+ static const size_t kStackOverflowImplicitCheckSize;
// Creates a new native thread corresponding to the given managed peer.
// Used to implement Thread.start.
@@ -551,6 +550,16 @@
return tlsPtr_.stack_size - (tlsPtr_.stack_end - tlsPtr_.stack_begin);
}
+ byte* GetStackEndForInterpreter(bool implicit_overflow_check) const {
+ if (implicit_overflow_check) {
+ // The interpreter needs the extra overflow bytes that stack_end does
+ // not include.
+ return tlsPtr_.stack_end + GetStackOverflowReservedBytes(kRuntimeISA);
+ } else {
+ return tlsPtr_.stack_end;
+ }
+ }
+
byte* GetStackEnd() const {
return tlsPtr_.stack_end;
}
@@ -567,7 +576,7 @@
// overflow region.
tlsPtr_.stack_end = tlsPtr_.stack_begin + kStackOverflowImplicitCheckSize;
} else {
- tlsPtr_.stack_end = tlsPtr_.stack_begin + kRuntimeStackOverflowReservedBytes;
+ tlsPtr_.stack_end = tlsPtr_.stack_begin + GetStackOverflowReservedBytes(kRuntimeISA);
}
}
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 8b1ad39..52cdcc1 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1154,22 +1154,55 @@
}
const char* GetAndroidData() {
+ std::string error_msg;
+ const char* dir = GetAndroidDataSafe(&error_msg);
+ if (dir != nullptr) {
+ return dir;
+ } else {
+ LOG(FATAL) << error_msg;
+ return "";
+ }
+}
+
+const char* GetAndroidDataSafe(std::string* error_msg) {
const char* android_data = getenv("ANDROID_DATA");
if (android_data == NULL) {
if (OS::DirectoryExists("/data")) {
android_data = "/data";
} else {
- LOG(FATAL) << "ANDROID_DATA not set and /data does not exist";
- return "";
+ *error_msg = "ANDROID_DATA not set and /data does not exist";
+ return nullptr;
}
}
if (!OS::DirectoryExists(android_data)) {
- LOG(FATAL) << "Failed to find ANDROID_DATA directory " << android_data;
- return "";
+ *error_msg = StringPrintf("Failed to find ANDROID_DATA directory %s", android_data);
+ return nullptr;
}
return android_data;
}
+void GetDalvikCache(const char* subdir, const bool create_if_absent, std::string* dalvik_cache,
+ bool* have_android_data, bool* dalvik_cache_exists) {
+ CHECK(subdir != nullptr);
+ std::string error_msg;
+ const char* android_data = GetAndroidDataSafe(&error_msg);
+ if (android_data == nullptr) {
+ *have_android_data = false;
+ *dalvik_cache_exists = false;
+ return;
+ } else {
+ *have_android_data = true;
+ }
+ const std::string dalvik_cache_root(StringPrintf("%s/dalvik-cache/", android_data));
+ *dalvik_cache = dalvik_cache_root + subdir;
+ *dalvik_cache_exists = OS::DirectoryExists(dalvik_cache->c_str());
+ if (create_if_absent && !*dalvik_cache_exists && strcmp(android_data, "/data") != 0) {
+ // Don't create the system's /data/dalvik-cache/... because it needs special permissions.
+ *dalvik_cache_exists = ((mkdir(dalvik_cache_root.c_str(), 0700) == 0 || errno == EEXIST) &&
+ (mkdir(dalvik_cache->c_str(), 0700) == 0 || errno == EEXIST));
+ }
+}
+
std::string GetDalvikCacheOrDie(const char* subdir, const bool create_if_absent) {
CHECK(subdir != nullptr);
const char* android_data = GetAndroidData();
@@ -1196,9 +1229,11 @@
return dalvik_cache;
}
-std::string GetDalvikCacheFilenameOrDie(const char* location, const char* cache_location) {
+bool GetDalvikCacheFilename(const char* location, const char* cache_location,
+ std::string* filename, std::string* error_msg) {
if (location[0] != '/') {
- LOG(FATAL) << "Expected path in location to be absolute: "<< location;
+ *error_msg = StringPrintf("Expected path in location to be absolute: %s", location);
+ return false;
}
std::string cache_file(&location[1]); // skip leading slash
if (!EndsWith(location, ".dex") && !EndsWith(location, ".art")) {
@@ -1206,7 +1241,17 @@
cache_file += DexFile::kClassesDex;
}
std::replace(cache_file.begin(), cache_file.end(), '/', '@');
- return StringPrintf("%s/%s", cache_location, cache_file.c_str());
+ *filename = StringPrintf("%s/%s", cache_location, cache_file.c_str());
+ return true;
+}
+
+std::string GetDalvikCacheFilenameOrDie(const char* location, const char* cache_location) {
+ std::string ret;
+ std::string error_msg;
+ if (!GetDalvikCacheFilename(location, cache_location, &ret, &error_msg)) {
+ LOG(FATAL) << error_msg;
+ }
+ return ret;
}
static void InsertIsaDirectory(const InstructionSet isa, std::string* filename) {
diff --git a/runtime/utils.h b/runtime/utils.h
index c920050..73872d3 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -439,11 +439,22 @@
// Find $ANDROID_DATA, /data, or abort.
const char* GetAndroidData();
+// Find $ANDROID_DATA, /data, or return nullptr.
+const char* GetAndroidDataSafe(std::string* error_msg);
// Returns the dalvik-cache location, or dies trying. subdir will be
// appended to the cache location.
std::string GetDalvikCacheOrDie(const char* subdir, bool create_if_absent = true);
+// Return true if we found the dalvik cache and stored it in the dalvik_cache argument.
+// have_android_data will be set to true if we have an ANDROID_DATA that exists,
+// dalvik_cache_exists will be true if there is a dalvik-cache directory that is present.
+void GetDalvikCache(const char* subdir, bool create_if_absent, std::string* dalvik_cache,
+ bool* have_android_data, bool* dalvik_cache_exists);
+// Returns the absolute dalvik-cache path for a DexFile or OatFile. The path returned will be
+// rooted at cache_location.
+bool GetDalvikCacheFilename(const char* file_location, const char* cache_location,
+ std::string* filename, std::string* error_msg);
// Returns the absolute dalvik-cache path for a DexFile or OatFile, or
// dies trying. The path returned will be rooted at cache_location.
std::string GetDalvikCacheFilenameOrDie(const char* file_location,
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 82c196d..de792cb 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3498,10 +3498,12 @@
value_compatible = value_type.IsFloatTypes();
} else if (target_type.IsLong()) {
instruction_compatible = insn_type.IsLong();
- value_compatible = value_type.IsLongTypes();
+ const RegType& value_type_hi = work_line_->GetRegisterType(vregA + 1);
+ value_compatible = value_type.IsLongTypes() && value_type.CheckWidePair(value_type_hi);
} else if (target_type.IsDouble()) {
instruction_compatible = insn_type.IsLong(); // no put-double, so expect put-long
- value_compatible = value_type.IsDoubleTypes();
+ const RegType& value_type_hi = work_line_->GetRegisterType(vregA + 1);
+ value_compatible = value_type.IsDoubleTypes() && value_type.CheckWidePair(value_type_hi);
} else {
instruction_compatible = false; // reference with primitive store
value_compatible = false; // unused
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index fdc6e3f..3a6a72b 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -47,6 +47,8 @@
jclass WellKnownClasses::java_lang_ThreadGroup;
jclass WellKnownClasses::java_lang_Throwable;
jclass WellKnownClasses::java_nio_DirectByteBuffer;
+jclass WellKnownClasses::java_util_Collections;
+jclass WellKnownClasses::libcore_util_EmptyArray;
jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk;
jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer;
@@ -150,6 +152,8 @@
java_lang_ThreadGroup = CacheClass(env, "java/lang/ThreadGroup");
java_lang_Throwable = CacheClass(env, "java/lang/Throwable");
java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer");
+ java_util_Collections = CacheClass(env, "java/util/Collections");
+ libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray");
org_apache_harmony_dalvik_ddmc_Chunk = CacheClass(env, "org/apache/harmony/dalvik/ddmc/Chunk");
org_apache_harmony_dalvik_ddmc_DdmServer = CacheClass(env, "org/apache/harmony/dalvik/ddmc/DdmServer");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index f6c2930..7639f50 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -60,7 +60,9 @@
static jclass java_lang_ThreadGroup;
static jclass java_lang_Thread$UncaughtExceptionHandler;
static jclass java_lang_Throwable;
+ static jclass java_util_Collections;
static jclass java_nio_DirectByteBuffer;
+ static jclass libcore_util_EmptyArray;
static jclass org_apache_harmony_dalvik_ddmc_Chunk;
static jclass org_apache_harmony_dalvik_ddmc_DdmServer;
diff --git a/test/018-stack-overflow/expected.txt b/test/018-stack-overflow/expected.txt
index 98b45b7..cc10c0c 100644
--- a/test/018-stack-overflow/expected.txt
+++ b/test/018-stack-overflow/expected.txt
@@ -1,3 +1,10 @@
-caught SOE in testSelfRecursion
+libartd run.
+caught SOE3 in testSelfRecursion
+caught SOE10 in testSelfRecursion
+caught SOE in testMutualRecursion
+SOE test done
+libart run.
+caught SOE3 in testSelfRecursion
+caught SOE10 in testSelfRecursion
caught SOE in testMutualRecursion
SOE test done
diff --git a/test/018-stack-overflow/run b/test/018-stack-overflow/run
new file mode 100755
index 0000000..1a71a1a
--- /dev/null
+++ b/test/018-stack-overflow/run
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+# Run normal. This will be the debug build.
+echo "libartd run."
+${RUN} "${@}"
+
+# Run non-debug.
+echo "libart run."
+${RUN} "${@/#libartd.so/libart.so}"
diff --git a/test/018-stack-overflow/src/Main.java b/test/018-stack-overflow/src/Main.java
index 41adabc..0961226 100644
--- a/test/018-stack-overflow/src/Main.java
+++ b/test/018-stack-overflow/src/Main.java
@@ -25,16 +25,38 @@
}
private static void testSelfRecursion() {
+// try {
+// stackOverflowTestSub0();
+// }
+// catch (StackOverflowError soe) {
+// System.out.println("caught SOE0 in testSelfRecursion");
+// }
try {
- stackOverflowTestSub(0.0, 0.0, 0.0);
+ stackOverflowTestSub3(0.0, 1.0, 2.0);
}
catch (StackOverflowError soe) {
- System.out.println("caught SOE in testSelfRecursion");
+ System.out.println("caught SOE3 in testSelfRecursion");
+ }
+ try {
+ stackOverflowTestSub10(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+ }
+ catch (StackOverflowError soe) {
+ System.out.println("caught SOE10 in testSelfRecursion");
}
}
- private static void stackOverflowTestSub(double pad1, double pad2, double pad3) {
- stackOverflowTestSub(pad1, pad2, pad3);
+ private static void stackOverflowTestSub0() {
+ stackOverflowTestSub0();
+ }
+
+ private static void stackOverflowTestSub3(double pad1, double pad2, double pad3) {
+ stackOverflowTestSub3(pad1, pad2, pad3);
+ }
+
+ private static void stackOverflowTestSub10(double pad1, double pad2, double pad3, double pad4,
+ double pad5, double pad6, double pad7, double pad8,
+ double pad9, double pad10) {
+ stackOverflowTestSub10(pad1, pad2, pad3, pad4, pad5, pad6, pad7, pad8, pad9, pad10);
}
private static void testMutualRecursion() {
diff --git a/test/050-sync-test/src/Main.java b/test/050-sync-test/src/Main.java
index ec6d732..5364e2a 100644
--- a/test/050-sync-test/src/Main.java
+++ b/test/050-sync-test/src/Main.java
@@ -50,13 +50,14 @@
one = new CpuThread(1);
two = new CpuThread(2);
- one.start();
-
- try {
- Thread.sleep(100);
- } catch (InterruptedException ie) {
- System.out.println("INTERRUPT!");
- ie.printStackTrace();
+ synchronized (one) {
+ one.start();
+ try {
+ one.wait();
+ } catch (InterruptedException ie) {
+ System.out.println("INTERRUPT!");
+ ie.printStackTrace();
+ }
}
two.start();
@@ -101,6 +102,9 @@
//System.out.println(Thread.currentThread().getName());
synchronized (mSyncable) {
+ synchronized (this) {
+ this.notify();
+ }
for (int i = 0; i < 10; i++) {
output(mNumber);
}
diff --git a/test/401-optimizing-compiler/src/Main.java b/test/401-optimizing-compiler/src/Main.java
index 0d8eeb9..2c6d1c2 100644
--- a/test/401-optimizing-compiler/src/Main.java
+++ b/test/401-optimizing-compiler/src/Main.java
@@ -85,6 +85,27 @@
Object o = new byte[1024];
}
}
+
+ // Test that we do NPE checks on invokedirect.
+ Exception exception = null;
+ try {
+ invokePrivate();
+ } catch (NullPointerException e) {
+ exception = e;
+ }
+
+ if (exception == null) {
+ throw new Error("Missing NullPointerException");
+ }
+ }
+
+ public static void invokePrivate() {
+ Main m = null;
+ m.privateMethod();
+ }
+
+ private void privateMethod() {
+ Object o = new Object();
}
static int $opt$TestInvokeIntParameter(int param) {
diff --git a/test/Android.oat.mk b/test/Android.oat.mk
index 8d31f8d..a83c527 100644
--- a/test/Android.oat.mk
+++ b/test/Android.oat.mk
@@ -106,19 +106,21 @@
# Define rules to run oat tests on the target.
# $(1): directory - the name of the test we're building such as HelloWorld.
# $(2): 2ND_ or undefined - used to differentiate between the primary and secondary architecture.
+# $(3): additional options
+# $(4): name-addition
define define-test-art-oat-rules-target
# Define a phony rule to run a target oat test using the default compiler.
- default_test_rule := test-art-target-oat-default-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
- $(call define-test-art-oat-rule-target,$(1),$(2),$$(default_test_rule),)
+ default_test_rule := test-art-target-oat-default$(4)-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
+ $(call define-test-art-oat-rule-target,$(1),$(2),$$(default_test_rule),$(3))
ART_TEST_TARGET_OAT_DEFAULT$$($(2)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += $$(default_test_rule)
ART_TEST_TARGET_OAT_DEFAULT_RULES += $$(default_test_rule)
ART_TEST_TARGET_OAT_DEFAULT_$(1)_RULES += $$(default_test_rule)
- optimizing_test_rule := test-art-target-oat-optimizing-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
+ optimizing_test_rule := test-art-target-oat-optimizing$(4)-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
ifeq ($$(ART_TEST_OPTIMIZING),true)
$(call define-test-art-oat-rule-target,$(1),$(2),$$(optimizing_test_rule), \
- -Xcompiler-option --compiler-backend=Optimizing)
+ -Xcompiler-option --compiler-backend=Optimizing $(3))
else
.PHONY: $$(optimizing_test_rule)
$$(optimizing_test_rule):
@@ -130,15 +132,15 @@
ART_TEST_TARGET_OAT_OPTIMIZING_$(1)_RULES += $$(optimizing_test_rule)
# Define a phony rule to run a target oat test using the interpeter.
- interpreter_test_rule := test-art-target-oat-interpreter-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
- $(call define-test-art-oat-rule-target,$(1),$(2),$$(interpreter_test_rule),-Xint)
+ interpreter_test_rule := test-art-target-oat-interpreter$(4)-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
+ $(call define-test-art-oat-rule-target,$(1),$(2),$$(interpreter_test_rule),-Xint $(3))
ART_TEST_TARGET_OAT_INTERPRETER$$($(2)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += $$(interpreter_test_rule)
ART_TEST_TARGET_OAT_INTERPRETER_RULES += $$(interpreter_test_rule)
ART_TEST_TARGET_OAT_INTERPRETER_$(1)_RULES += $$(interpreter_test_rule)
# Define a phony rule to run both the default and interpreter variants.
- all_test_rule := test-art-target-oat-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
+ all_test_rule := test-art-target-oat$(4)-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
.PHONY: $$(all_test_rule)
$$(all_test_rule): $$(default_test_rule) $$(optimizing_test_rule) $$(interpreter_test_rule)
$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
@@ -200,7 +202,7 @@
$(hide) mkdir -p $(ART_HOST_TEST_DIR)/android-data-$$@/dalvik-cache/$$($(2)HOST_ARCH)
$(hide) cp $$(realpath $$<) $(ART_HOST_TEST_DIR)/android-data-$$@/oat-test-dex-$(1).jar
$(hide) $(DEX2OATD) $(DEX2OAT_FLAGS) --runtime-arg -Xms$(DEX2OAT_XMS) --runtime-arg -Xmx$(DEX2OAT_XMX) $(4) \
- --boot-image=$$(HOST_CORE_IMG_LOCATION) \
+ --boot-image=$$(HOST_CORE_IMG_LOCATION) --include-patch-information \
--dex-file=$$(PRIVATE_DEX_FILE) --oat-file=$$(PRIVATE_OAT_FILE) \
--instruction-set=$($(2)ART_HOST_ARCH) --host --android-root=$(HOST_OUT) \
|| $$(call ART_TEST_FAILED,$$@)
@@ -351,9 +353,21 @@
ART_TEST_TARGET_OAT_OPTIMIZING_$(1)_RULES :=
ART_TEST_TARGET_OAT_INTERPRETER_$(1)_RULES :=
ART_TEST_TARGET_OAT_$(1)_RULES :=
- $(call define-test-art-oat-rules-target,$(1),)
- ifdef TARGET_2ND_ARCH
- $(call define-test-art-oat-rules-target,$(1),2ND_)
+ ifeq ($(ART_TEST_OAT_NO_RELOCATE),true)
+ $(call define-test-art-oat-rules-target,$(1),, \
+ -Xnorelocate -Xcompiler-option --no-include-patch-information,-norelocate)
+ ifdef TARGET_2ND_ARCH
+ $(call define-test-art-oat-rules-target,$(1),2ND_, \
+ -Xnorelocate -Xcompiler-option --no-include-patch-information,-norelocate)
+ endif
+ endif
+ ifeq ($(ART_TEST_OAT_RELOCATE),true)
+ $(call define-test-art-oat-rules-target,$(1),, \
+ -Xrelocate -Xcompiler-option --include-patch-information,-relocate)
+ ifdef TARGET_2ND_ARCH
+ $(call define-test-art-oat-rules-target,$(1),2ND_, \
+ -Xrelocate -Xcompiler-option --include-patch-information,-relocate)
+ endif
endif
$(call define-test-art-oat-combination-for-test,$(1),target,TARGET,,))
$(call define-test-art-oat-combination-for-test,$(1),target,TARGET,-default,_DEFAULT))
@@ -456,6 +470,22 @@
$(eval $(call define-test-art-oat-combination,host,HOST,-interpreter,_INTERPRETER,$(2ND_ART_PHONY_TEST_HOST_SUFFIX)))
endif
+# List -relocate version on the target as broken.
+
+# List all the test names for target and compiler variants.
+# $(1): test name, e.g. Main
+# $(2): -relocate, -norelocate, -prebuild, or undefined.
+define all-oat-test-target-names
+ test-art-target-oat-default$(2)-$(1)32 \
+ test-art-target-oat-optimizing$(2)-$(1)32 \
+ test-art-target-oat-interpreter$(2)-$(1)32 \
+ test-art-target-oat-default$(2)-$(1)64 \
+ test-art-target-oat-optimizing$(2)-$(1)64 \
+ test-art-target-oat-interpreter$(2)-$(1)64
+endef # all-oat-test-target-names
+
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_OAT_DIRECTORIES), $(call all-oat-test-target-names,$(test),-relocate))
+
# Clear locally defined variables.
define-test-art-oat-rule-target :=
define-test-art-oat-rules-target :=
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index b218c24..a663b8b 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -24,19 +24,20 @@
# List all the test names for host and target and compiler variants.
# $(1): test name, e.g. 003-omnibus-opcodes
# $(2): undefined, -trace, -gcverify or -gcstress
+# $(3): -relocate, -norelocate, -prebuild, or undefined.
define all-run-test-names
- test-art-host-run-test$(2)-default-$(1)32 \
- test-art-host-run-test$(2)-optimizing-$(1)32 \
- test-art-host-run-test$(2)-interpreter-$(1)32 \
- test-art-host-run-test$(2)-default-$(1)64 \
- test-art-host-run-test$(2)-optimizing-$(1)64 \
- test-art-host-run-test$(2)-interpreter-$(1)64 \
- test-art-target-run-test$(2)-default-$(1)32 \
- test-art-target-run-test$(2)-optimizing-$(1)32 \
- test-art-target-run-test$(2)-interpreter-$(1)32 \
- test-art-target-run-test$(2)-default-$(1)64 \
- test-art-target-run-test$(2)-optimizing-$(1)64 \
- test-art-target-run-test$(2)-interpreter-$(1)64
+ test-art-host-run-test$(2)-default$(3)-$(1)32 \
+ test-art-host-run-test$(2)-optimizing$(3)-$(1)32 \
+ test-art-host-run-test$(2)-interpreter$(3)-$(1)32 \
+ test-art-host-run-test$(2)-default$(3)-$(1)64 \
+ test-art-host-run-test$(2)-optimizing$(3)-$(1)64 \
+ test-art-host-run-test$(2)-interpreter$(3)-$(1)64 \
+ test-art-target-run-test$(2)-default$(3)-$(1)32 \
+ test-art-target-run-test$(2)-optimizing$(3)-$(1)32 \
+ test-art-target-run-test$(2)-interpreter$(3)-$(1)32 \
+ test-art-target-run-test$(2)-default$(3)-$(1)64 \
+ test-art-target-run-test$(2)-optimizing$(3)-$(1)64 \
+ test-art-target-run-test$(2)-interpreter$(3)-$(1)64
endef # all-run-test-names
# Tests that are timing sensitive and flaky on heavily loaded systems.
@@ -46,10 +47,22 @@
# disable timing sensitive tests on "dist" builds.
ifdef dist_goal
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,-relocate))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-relocate))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,-relocate))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-relocate))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,-norelocate))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-norelocate))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,-norelocate))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-norelocate))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,-prebuild))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-prebuild))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,-prebuild))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-prebuild))
endif
# Tests that are broken in --trace mode.
@@ -83,7 +96,10 @@
700-LoadArgRegs \
701-easy-div-rem
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-relocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-norelocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,))
# Tests that need more than 2MB of RAM or are running into other corner cases in GC stress related
# to OOMEs.
@@ -92,7 +108,10 @@
080-oom-throw \
096-array-copy-concurrent-gc
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-relocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-norelocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,))
# The path where build only targets will be output, e.g.
@@ -142,26 +161,96 @@
ART_TEST_TARGET_RUN_TEST_DEFAULT_RULES :=
ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES :=
ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RULES :=
+ART_TEST_TARGET_RUN_TEST_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
ART_TEST_TARGET_RUN_TEST_ALL$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_ALL_RULES :=
ART_TEST_HOST_RUN_TEST_DEFAULT_RULES :=
ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES :=
ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES :=
+ART_TEST_HOST_RUN_TEST_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
ART_TEST_HOST_RUN_TEST_ALL$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
# We need dex2oat and dalvikvm on the target as well as the core image.
TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUT) $(2ND_TARGET_CORE_IMG_OUT)
@@ -185,12 +274,14 @@
# $(3): default, optimizing or interpreter
# $(4): 32 or 64
# $(5): run tests with tracing or GC verification enabled or not: trace, gcverify or undefined
+# $(6): relocate, norelocate, prebuild
define define-test-art-run-test
run_test_options := $(addprefix --runtime-option ,$(DALVIKVM_FLAGS))
- run_test_rule_name := test-art-$(2)-run-test-$(3)-$(1)$(4)
+ run_test_rule_name :=
uc_host_or_target :=
prereq_rule :=
skip_test := false
+ uc_reloc_type :=
ifeq ($(2),host)
uc_host_or_target := HOST
run_test_options += --host
@@ -203,6 +294,31 @@
$$(error found $(2) expected host or target)
endif
endif
+ ifeq ($(6),relocate)
+ uc_reloc_type := RELOCATE
+ run_test_options += --relocate
+ ifneq ($(ART_TEST_RUN_TEST_RELOCATE),true)
+ skip_test := true
+ endif
+ else
+ ifeq ($(6),prebuild)
+ uc_reloc_type := PREBUILD
+ run_test_options += --relocate --prebuild
+ ifneq ($(ART_TEST_RUN_TEST_PREBUILD),true)
+ skip_test := true
+ endif
+ else
+ ifeq ($(6),norelocate)
+ uc_reloc_type := NORELOCATE
+ run_test_options += --no-relocate
+ ifneq ($(ART_TEST_RUN_TEST_NO_RELOCATE),true)
+ skip_test := true
+ endif
+ else
+ $$(error found $(6) expected relocate, norelocate or prebuild)
+ endif
+ endif
+ endif
uc_compiler :=
ifeq ($(3),optimizing)
uc_compiler := OPTIMIZING
@@ -231,7 +347,7 @@
endif
ifeq ($(5),trace)
run_test_options += --trace
- run_test_rule_name := test-art-$(2)-run-test-trace-$(3)-$(1)$(4)
+ run_test_rule_name := test-art-$(2)-run-test-trace-$(3)-$(6)-$(1)$(4)
ifneq ($$(ART_TEST_TRACE),true)
skip_test := true
endif
@@ -239,7 +355,7 @@
ifeq ($(5),gcverify)
run_test_options += --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify \
--runtime-option -Xgc:preverify_rosalloc --runtime-option -Xgc:postverify_rosalloc
- run_test_rule_name := test-art-$(2)-run-test-gcverify-$(3)-$(1)$(4)
+ run_test_rule_name := test-art-$(2)-run-test-gcverify-$(3)-$(6)-$(1)$(4)
ifneq ($$(ART_TEST_GC_VERIFY),true)
skip_test := true
endif
@@ -247,7 +363,7 @@
ifeq ($(5),gcstress)
run_test_options += --runtime-option -Xgc:SS --runtime-option -Xms2m \
--runtime-option -Xmx2m --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify
- run_test_rule_name := test-art-$(2)-run-test-gcstress-$(3)-$(1)$(4)
+ run_test_rule_name := test-art-$(2)-run-test-gcstress-$(3)-$(6)-$(1)$(4)
ifneq ($$(ART_TEST_GC_STRESS),true)
skip_test := true
endif
@@ -255,6 +371,7 @@
ifneq (,$(5))
$$(error found $(5) expected undefined or gcverify, gcstress or trace)
endif
+ run_test_rule_name := test-art-$(2)-run-test-$(3)-$(6)-$(1)$(4)
endif
endif
endif
@@ -279,9 +396,10 @@
ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)$(4)_RULES += $$(run_test_rule_name)
ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_RULES += $$(run_test_rule_name)
ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_$(1)_RULES += $$(run_test_rule_name)
- ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_RULES += $$(run_test_rule_name)
+ ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_$$(uc_reloc_type)_RULES += $$(run_test_rule_name)
ART_TEST_$$(uc_host_or_target)_RUN_TEST_$(1)_RULES += $$(run_test_rule_name)
ART_TEST_$$(uc_host_or_target)_RUN_TEST_ALL_RULES += $$(run_test_rule_name)
+ ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_reloc_type)_RULES += $$(run_test_rule_name)
ART_TEST_$$(uc_host_or_target)_RUN_TEST_ALL$(4)_RULES += $$(run_test_rule_name)
# Clear locally defined variables.
@@ -290,6 +408,7 @@
run_test_rule_name :=
uc_host_or_target :=
prereq_rule :=
+ uc_reloc_type :=
uc_compiler :=
endef # define-test-art-run-test
@@ -306,7 +425,8 @@
# Create rules for a group of run tests.
# $(1): test name, e.g. 003-omnibus-opcodes
# $(2): host or target
-define define-test-art-run-test-group
+# $(3): relocate, norelocate or prebuild
+define define-test-art-run-test-group-type
group_uc_host_or_target :=
ifeq ($(2),host)
group_uc_host_or_target := HOST
@@ -322,18 +442,18 @@
ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_INTERPRETER_$(1)_RULES :=
ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_OPTIMIZING_$(1)_RULES :=
ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)_RULES :=
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
do_second := false
ifeq ($(2),host)
ifneq ($$(HOST_PREFER_32_BIT),true)
@@ -345,20 +465,42 @@
endif
endif
ifeq (true,$$(do_second))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
endif
+endef # define-test-art-run-test-group-type
+# Create rules for a group of run tests.
+# $(1): test name, e.g. 003-omnibus-opcodes
+# $(2): host or target
+define define-test-art-run-test-group
+ group_uc_host_or_target :=
+ ifeq ($(2),host)
+ group_uc_host_or_target := HOST
+ else
+ ifeq ($(2),target)
+ group_uc_host_or_target := TARGET
+ else
+ $$(error found $(2) expected host or target)
+ endif
+ endif
+ ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_DEFAULT_$(1)_RULES :=
+ ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_INTERPRETER_$(1)_RULES :=
+ ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_OPTIMIZING_$(1)_RULES :=
+ ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)_RULES :=
+ $$(eval $$(call define-test-art-run-test-group-type,$(1),$(2),norelocate))
+ $$(eval $$(call define-test-art-run-test-group-type,$(1),$(2),relocate))
+ $$(eval $$(call define-test-art-run-test-group-type,$(1),$(2),prebuild))
$$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-default-$(1), \
$$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_DEFAULT_$(1)_RULES)))
$$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-interpreter-$(1), \
@@ -380,6 +522,12 @@
$(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call define-test-art-run-test-group,$(test),target)))
$(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call define-test-art-run-test-group,$(test),host)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-prebuild, \
+ $(ART_TEST_TARGET_RUN_TEST_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-norelocate, \
+ $(ART_TEST_TARGET_RUN_TEST_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-relocate, \
+ $(ART_TEST_TARGET_RUN_TEST_RELOCATE_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test, \
$(ART_TEST_TARGET_RUN_TEST_ALL_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default, \
@@ -388,6 +536,24 @@
$(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing, \
$(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-prebuild, \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-prebuild, \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-prebuild, \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-norelocate, \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-norelocate, \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-norelocate, \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-relocate, \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-relocate, \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-relocate, \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test$(ART_PHONY_TEST_TARGET_SUFFIX), \
$(ART_TEST_TARGET_RUN_TEST_ALL$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default$(ART_PHONY_TEST_TARGET_SUFFIX), \
@@ -396,6 +562,30 @@
$(ART_TEST_TARGET_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX), \
$(ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
ifdef TARGET_2ND_ARCH
$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
$(ART_TEST_TARGET_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
@@ -405,8 +595,38 @@
$(ART_TEST_TARGET_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
$(ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
endif
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-prebuild, \
+ $(ART_TEST_HOST_RUN_TEST_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-norelocate, \
+ $(ART_TEST_HOST_RUN_TEST_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-relocate, \
+ $(ART_TEST_HOST_RUN_TEST_RELOCATE_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test, \
$(ART_TEST_HOST_RUN_TEST_ALL_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default, \
@@ -415,6 +635,24 @@
$(ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing, \
$(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-prebuild, \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-prebuild, \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-prebuild, \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-norelocate, \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-norelocate, \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-norelocate, \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-relocate, \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-relocate, \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-relocate, \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test$(ART_PHONY_TEST_HOST_SUFFIX), \
$(ART_TEST_HOST_RUN_TEST_ALL$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default$(ART_PHONY_TEST_HOST_SUFFIX), \
@@ -423,6 +661,30 @@
$(ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing$(ART_PHONY_TEST_HOST_SUFFIX), \
$(ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
ifneq ($(HOST_PREFER_32_BIT),true)
$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
$(ART_TEST_HOST_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
@@ -432,6 +694,30 @@
$(ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
$(ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
endif
define-test-art-run-test :=
@@ -442,23 +728,93 @@
ART_TEST_TARGET_RUN_TEST_DEFAULT_RULES :=
ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES :=
ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RULES :=
+ART_TEST_TARGET_RUN_TEST_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
ART_TEST_TARGET_RUN_TEST_ALL$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_ALL_RULES :=
ART_TEST_HOST_RUN_TEST_DEFAULT_RULES :=
ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES :=
ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES :=
+ART_TEST_HOST_RUN_TEST_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
ART_TEST_HOST_RUN_TEST_ALL$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
diff --git a/test/etc/host-run-test-jar b/test/etc/host-run-test-jar
index 4265f1c..4d83c87 100755
--- a/test/etc/host-run-test-jar
+++ b/test/etc/host-run-test-jar
@@ -10,20 +10,28 @@
}
DEBUGGER="n"
+PREBUILD="n"
GDB="n"
+ISA="x86"
INTERPRETER="n"
VERIFY="y"
+RELOCATE="y"
OPTIMIZE="y"
INVOKE_WITH=""
DEV_MODE="n"
QUIET="n"
FLAGS=""
+COMPILER_FLAGS=""
+BUILD_BOOT_OPT=""
exe="${ANDROID_HOST_OUT}/bin/dalvikvm32"
while true; do
if [ "x$1" = "x--quiet" ]; then
QUIET="y"
shift
+ elif [ "x$1" = "x--prebuild" ]; then
+ PREBUILD="y"
+ shift
elif [ "x$1" = "x--lib" ]; then
shift
if [ "x$1" = "x" ]; then
@@ -37,7 +45,9 @@
shift
elif [ "x$1" = "x--boot" ]; then
shift
- BOOT_OPT="$1"
+ option="$1"
+ BOOT_OPT="$option"
+ BUILD_BOOT_OPT="--boot-image=${option#-Ximage:}"
shift
elif [ "x$1" = "x--debug" ]; then
DEBUGGER="y"
@@ -65,6 +75,7 @@
INTERPRETER="y"
shift
elif [ "x$1" = "x--64" ]; then
+ ISA="x64"
exe="${ANDROID_HOST_OUT}/bin/dalvikvm64"
shift
elif [ "x$1" = "x--no-verify" ]; then
@@ -73,15 +84,23 @@
elif [ "x$1" = "x--no-optimize" ]; then
OPTIMIZE="n"
shift
+ elif [ "x$1" = "x--no-relocate" ]; then
+ RELOCATE="n"
+ shift
+ elif [ "x$1" = "x--relocate" ]; then
+ RELOCATE="y"
+ shift
elif [ "x$1" = "x-Xcompiler-option" ]; then
shift
option="$1"
FLAGS="${FLAGS} -Xcompiler-option $option"
+ COMPILER_FLAGS="${COMPILER_FLAGS} $option"
shift
elif [ "x$1" = "x--runtime-option" ]; then
shift
option="$1"
FLAGS="${FLAGS} $option"
+ COMPILER_FLAGS="${COMPILER_FLAGS} --runtime-arg $option"
shift
elif [ "x$1" = "x--" ]; then
shift
@@ -129,13 +148,40 @@
if [ "$INTERPRETER" = "y" ]; then
INT_OPTS="-Xint"
+ COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=interpret-only"
+fi
+
+if [ "$RELOCATE" = "y" ]; then
+ FLAGS="${FLAGS} -Xrelocate"
+ COMPILER_FLAGS="${COMPILER_FLAGS} --runtime-arg -Xnorelocate --include-patch-information"
+ # Run test sets a fairly draconian ulimit that we will likely blow right over
+ # since we are relocating. Get the total size of the /system/framework directory
+ # in 512 byte blocks and set it as the ulimit. This should be more than enough
+ # room.
+ ulimit -S $(du -c -B512 ${ANDROID_ROOT}/framework | tail -1 | cut -f1) || exit 1
+else
+ FLAGS="${FLAGS} -Xnorelocate"
+ COMPILER_FLAGS="${COMPILER_FLAGS} --runtime-arg -Xnorelocate --no-include-patch-information"
+fi
+
+mkdir_cmd="mkdir -p ${DEX_LOCATION}/dalvik-cache/$ISA"
+if [ "$PREBUILD" = "y" ]; then
+ prebuild_cmd="${ANDROID_HOST_OUT}/bin/dex2oatd $COMPILER_FLAGS --instruction-set=$ISA $BUILD_BOOT_OPT --dex-file=$DEX_LOCATION/$TEST_NAME.jar --oat-file=$DEX_LOCATION/dalvik-cache/$ISA/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.dex | cut -d/ -f 2- | sed "s:/:@:g")"
+else
+ prebuild_cmd="true"
fi
JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
-
+cmdline="$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB $JNI_OPTS $FLAGS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main"
if [ "$DEV_MODE" = "y" ]; then
- echo $cmdline "$@"
+ if [ "$PREBUILD" = "y" ]; then
+ echo "$mkdir_cmd && $prebuild_cmd && $cmdline"
+ elif [ "$RELOCATE" = "y" ]; then
+ echo "$mkdir_cmd && $cmdline"
+ else
+ echo $cmdline
+ fi
fi
cd $ANDROID_BUILD_TOP
-$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB $JNI_OPTS $FLAGS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main "$@"
+$mkdir_cmd && $prebuild_cmd && $cmdline "$@"
diff --git a/test/etc/push-and-run-prebuilt-test-jar b/test/etc/push-and-run-prebuilt-test-jar
new file mode 100755
index 0000000..dd2ceeb
--- /dev/null
+++ b/test/etc/push-and-run-prebuilt-test-jar
@@ -0,0 +1,212 @@
+#!/bin/sh
+#
+# Run the code in test.jar on the device. The jar should contain a top-level
+# class named Main to run.
+
+msg() {
+ if [ "$QUIET" = "n" ]; then
+ echo "$@"
+ fi
+}
+
+ARCHITECTURES_32="(arm|x86|mips|none)"
+ARCHITECTURES_64="(arm64|x86_64|none)"
+ARCHITECTURES_PATTERN="${ARCHITECTURES_32}"
+RELOCATE="y"
+GDB="n"
+DEBUGGER="n"
+INTERPRETER="n"
+VERIFY="y"
+OPTIMIZE="y"
+ZYGOTE=""
+QUIET="n"
+DEV_MODE="n"
+INVOKE_WITH=""
+FLAGS=""
+TARGET_SUFFIX=""
+COMPILE_FLAGS=""
+
+while true; do
+ if [ "x$1" = "x--quiet" ]; then
+ QUIET="y"
+ shift
+ elif [ "x$1" = "x--lib" ]; then
+ shift
+ if [ "x$1" = "x" ]; then
+ echo "$0 missing argument to --lib" 1>&2
+ exit 1
+ fi
+ LIB="$1"
+ shift
+ elif [ "x$1" = "x-Xcompiler-option" ]; then
+ shift
+ option="$1"
+ FLAGS="${FLAGS} -Xcompiler-option $option"
+ COMPILE_FLAGS="${COMPILE_FLAGS} $option"
+ shift
+ elif [ "x$1" = "x--runtime-option" ]; then
+ shift
+ option="$1"
+ FLAGS="${FLAGS} $option"
+ COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg $option"
+ shift
+ elif [ "x$1" = "x--boot" ]; then
+ shift
+ BOOT_OPT="$1"
+ BUILD_BOOT_OPT="--boot-image=${1#-Ximage:}"
+ shift
+ elif [ "x$1" = "x--relocate" ]; then
+ RELOCATE="y"
+ shift
+ elif [ "x$1" = "x--no-relocate" ]; then
+ RELOCATE="n"
+ shift
+ elif [ "x$1" = "x--debug" ]; then
+ DEBUGGER="y"
+ shift
+ elif [ "x$1" = "x--gdb" ]; then
+ GDB="y"
+ DEV_MODE="y"
+ shift
+ elif [ "x$1" = "x--zygote" ]; then
+ ZYGOTE="--zygote"
+ msg "Spawning from zygote"
+ shift
+ elif [ "x$1" = "x--dev" ]; then
+ DEV_MODE="y"
+ shift
+ elif [ "x$1" = "x--interpreter" ]; then
+ INTERPRETER="y"
+ shift
+ elif [ "x$1" = "x--invoke-with" ]; then
+ shift
+ if [ "x$1" = "x" ]; then
+ echo "$0 missing argument to --invoke-with" 1>&2
+ exit 1
+ fi
+ if [ "x$INVOKE_WITH" = "x" ]; then
+ INVOKE_WITH="$1"
+ else
+ INVOKE_WITH="$INVOKE_WITH $1"
+ fi
+ shift
+ elif [ "x$1" = "x--no-verify" ]; then
+ VERIFY="n"
+ shift
+ elif [ "x$1" = "x--no-optimize" ]; then
+ OPTIMIZE="n"
+ shift
+ elif [ "x$1" = "x--" ]; then
+ shift
+ break
+ elif [ "x$1" = "x--64" ]; then
+ TARGET_SUFFIX="64"
+ ARCHITECTURES_PATTERN="${ARCHITECTURES_64}"
+ shift
+ elif expr "x$1" : "x--" >/dev/null 2>&1; then
+ echo "unknown $0 option: $1" 1>&2
+ exit 1
+ else
+ break
+ fi
+done
+
+if [ "$ZYGOTE" = "" ]; then
+ if [ "$OPTIMIZE" = "y" ]; then
+ if [ "$VERIFY" = "y" ]; then
+ DEX_OPTIMIZE="-Xdexopt:verified"
+ else
+ DEX_OPTIMIZE="-Xdexopt:all"
+ fi
+ msg "Performing optimizations"
+ else
+ DEX_OPTIMIZE="-Xdexopt:none"
+ msg "Skipping optimizations"
+ fi
+
+ if [ "$VERIFY" = "y" ]; then
+ DEX_VERIFY=""
+ msg "Performing verification"
+ else
+ DEX_VERIFY="-Xverify:none"
+ msg "Skipping verification"
+ fi
+fi
+
+msg "------------------------------"
+
+ARCH=$(adb shell ls -F /data/dalvik-cache | grep -Ewo "${ARCHITECTURES_PATTERN}")
+if [ x"$ARCH" = "x" ]; then
+ echo "Unable to determine architecture"
+ exit 1
+fi
+
+if [ "$QUIET" = "n" ]; then
+ adb shell rm -r $DEX_LOCATION
+ adb shell mkdir -p $DEX_LOCATION
+ adb push $TEST_NAME.jar $DEX_LOCATION
+ adb push $TEST_NAME-ex.jar $DEX_LOCATION
+else
+ adb shell rm -r $DEX_LOCATION >/dev/null 2>&1
+ adb shell mkdir -p $DEX_LOCATION >/dev/null 2>&1
+ adb push $TEST_NAME.jar $DEX_LOCATION >/dev/null 2>&1
+ adb push $TEST_NAME-ex.jar $DEX_LOCATION >/dev/null 2>&1
+fi
+
+if [ "$DEBUGGER" = "y" ]; then
+ # Use this instead for ddms and connect by running 'ddms':
+ # DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_android_adb,server=y,suspend=y"
+ # TODO: add a separate --ddms option?
+
+ PORT=12345
+ msg "Waiting for jdb to connect:"
+ msg " adb forward tcp:$PORT tcp:$PORT"
+ msg " jdb -attach localhost:$PORT"
+ DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
+fi
+
+if [ "$GDB" = "y" ]; then
+ gdb="gdbserver$TARGET_SUFFIX :5039"
+ gdbargs="$exe"
+fi
+
+if [ "$INTERPRETER" = "y" ]; then
+ INT_OPTS="-Xint"
+ COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=interpret-only"
+fi
+
+JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
+
+if [ "$RELOCATE" = "y" ]; then
+ RELOCATE_OPT="-Xrelocate"
+ BUILD_RELOCATE_OPT="--runtime-arg -Xnorelocate"
+ COMPILE_FLAGS="${COMPILE_FLAGS} --include-patch-information"
+ FLAGS="${FLAGS} -Xcompiler-option --include-patch-information"
+else
+ RELOCATE_OPT="-Xnorelocate"
+ BUILD_RELOCATE_OPT="--runtime-arg -Xnorelocate"
+fi
+
+# This is due to the fact this cmdline can get longer than the longest allowed
+# adb command and there is no way to get the exit status from a adb shell
+# command.
+cmdline="cd $DEX_LOCATION && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \
+ mkdir -p $DEX_LOCATION/dalvik-cache/$ARCH/ && \
+ $INVOKE_WITH /system/bin/dex2oatd $COMPILE_FLAGS $BUILD_BOOT_OPT $BUILD_RELOCATE_OPT --runtime-arg -classpath --runtime-arg $DEX_LOCATION/$TEST_NAME.jar --dex-file=$DEX_LOCATION/$TEST_NAME.jar --oat-file=$DEX_LOCATION/dalvik-cache/$ARCH/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.dex | cut -d/ -f 2- | sed "s:/:@:g") --instruction-set=$ARCH && \
+ $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main $@"
+cmdfile=$(tempfile -p "cmd-" -s "-$TEST_NAME")
+echo "$cmdline" > $cmdfile
+
+if [ "$DEV_MODE" = "y" ]; then
+ echo $cmdline
+fi
+
+if [ "$QUIET" = "n" ]; then
+ adb push $cmdfile $DEX_LOCATION/cmdline.sh
+else
+ adb push $cmdfile $DEX_LOCATION/cmdline.sh > /dev/null 2>&1
+fi
+
+adb shell sh $DEX_LOCATION/cmdline.sh
+
+rm -f $cmdfile
diff --git a/test/etc/push-and-run-test-jar b/test/etc/push-and-run-test-jar
index b090c33..776a011 100755
--- a/test/etc/push-and-run-test-jar
+++ b/test/etc/push-and-run-test-jar
@@ -9,6 +9,7 @@
fi
}
+RELOCATE="y"
GDB="n"
DEBUGGER="n"
INTERPRETER="n"
@@ -61,6 +62,12 @@
elif [ "x$1" = "x--dev" ]; then
DEV_MODE="y"
shift
+ elif [ "x$1" = "x--relocate" ]; then
+ RELOCATE="y"
+ shift
+ elif [ "x$1" = "x--no-relocate" ]; then
+ RELOCATE="n"
+ shift
elif [ "x$1" = "x--interpreter" ]; then
INTERPRETER="y"
shift
@@ -155,8 +162,15 @@
JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
+if [ "$RELOCATE" = "y" ]; then
+ RELOCATE_OPT="-Xrelocate"
+ FLAGS="${FLAGS} -Xcompiler-option --include-patch-information"
+else
+ RELOCATE_OPT="-Xnorelocate"
+fi
+
cmdline="cd $DEX_LOCATION && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \
- $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main"
+ $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main"
if [ "$DEV_MODE" = "y" ]; then
echo $cmdline "$@"
fi
diff --git a/test/run-all-tests b/test/run-all-tests
index 25d5c5f..c31fefe 100755
--- a/test/run-all-tests
+++ b/test/run-all-tests
@@ -83,6 +83,15 @@
elif [ "x$1" = "x--trace" ]; then
run_args="${run_args} --trace"
shift
+ elif [ "x$1" = "x--relocate" ]; then
+ run_args="${run_args} --relocate"
+ shift
+ elif [ "x$1" = "x--no-relocate" ]; then
+ run_args="${run_args} --no-relocate"
+ shift
+ elif [ "x$1" = "x--prebuild" ]; then
+ run_args="${run_args} --prebuild"
+ shift;
elif expr "x$1" : "x--" >/dev/null 2>&1; then
echo "unknown $0 option: $1" 1>&2
usage="yes"
@@ -101,7 +110,8 @@
echo " Options are all passed to run-test; refer to that for " \
"further documentation:"
echo " --debug --dev --host --interpreter --jvm --no-optimize"
- echo " --no-verify -O --update --valgrind --zygote --64"
+ echo " --no-verify -O --update --valgrind --zygote --64 --relocate"
+ echo " --prebuild"
echo " Specific Runtime Options:"
echo " --seq Run tests one-by-one, avoiding failures caused by busy CPU"
) 1>&2
diff --git a/test/run-test b/test/run-test
index 1df0ec2..5c38be3 100755
--- a/test/run-test
+++ b/test/run-test
@@ -61,9 +61,12 @@
lib="libartd.so"
run_args="--quiet"
+prebuild_mode="no"
target_mode="yes"
dev_mode="no"
update_mode="no"
+debug_mode="no"
+relocate="yes"
runtime="art"
usage="no"
build_only="no"
@@ -72,13 +75,11 @@
while true; do
if [ "x$1" = "x--host" ]; then
target_mode="no"
- RUN="${progdir}/etc/host-run-test-jar"
DEX_LOCATION=$tmp_dir
shift
elif [ "x$1" = "x--jvm" ]; then
target_mode="no"
runtime="jvm"
- RUN="${progdir}/etc/reference-run-test-classes"
NEED_DEX="false"
shift
elif [ "x$1" = "x-O" ]; then
@@ -88,6 +89,18 @@
lib="libdvm.so"
runtime="dalvik"
shift
+ elif [ "x$1" = "x--relocate" ]; then
+ relocate="yes"
+ shift
+ elif [ "x$1" = "x--no-relocate" ]; then
+ relocate="no"
+ shift
+ elif [ "x$1" = "x--prebuild" ]; then
+ prebuild_mode="yes"
+ shift;
+ elif [ "x$1" = "x--no-prebuild" ]; then
+ prebuild_mode="no"
+ shift;
elif [ "x$1" = "x--image" ]; then
shift
image="$1"
@@ -174,6 +187,25 @@
done
mkdir -p $tmp_dir
+if [ "$target_mode" = "no" ]; then
+ if [ "$runtime" = "jvm" ]; then
+ RUN="${progdir}/etc/reference-run-test-classes"
+ if [ "$prebuild_mode" = "yes" ]; then
+ echo "--prebuild with --jvm is unsupported";
+ exit 1;
+ fi
+ else
+ RUN="${progdir}/etc/host-run-test-jar"
+ if [ "$prebuild_mode" = "yes" ]; then
+ run_args="${run_args} --prebuild"
+ fi
+ fi
+else
+ if [ "$prebuild_mode" = "yes" ]; then
+ RUN="${progdir}/etc/push-and-run-prebuilt-test-jar"
+ fi
+fi
+
if [ ! "$runtime" = "jvm" ]; then
run_args="${run_args} --lib $lib"
fi
@@ -199,6 +231,11 @@
else
run_args="${run_args} --boot -Ximage:/data/art-test/core.art"
fi
+ if [ "$relocate" = "yes" ]; then
+ run_args="${run_args} --relocate"
+ else
+ run_args="${run_args} --no-relocate"
+ fi
fi
if [ "$dev_mode" = "yes" -a "$update_mode" = "yes" ]; then
@@ -252,6 +289,13 @@
echo " --zygote Spawn the process from the Zygote." \
"If used, then the"
echo " other runtime options are ignored."
+ echo " --prebuild Run dex2oat on the files before starting test."
+ echo " --no-prebuild Do not run dex2oat on the files before starting"
+ echo " the test. (default)"
+ echo " --relocate Force the use of relocating in the test, making"
+ echo " the image and oat files be relocated to a random"
+ echo " address before running. (default)"
+ echo " --no-relocate Force the use of no relocating in the test"
echo " --host Use the host-mode virtual machine."
echo " --invoke-with Pass --invoke-with option to runtime."
echo " --dalvik Use Dalvik (off by default)."
@@ -308,7 +352,7 @@
elif echo "$test_dir" | grep 083; then
file_size_limit=5120
fi
-if ! ulimit "$file_size_limit"; then
+if ! ulimit -S "$file_size_limit"; then
echo "ulimit file size setting failed"
fi