diff options
author | Nicolas Geoffray <ngeoffray@google.com> | 2014-07-11 08:26:40 +0000 |
---|---|---|
committer | Nicolas Geoffray <ngeoffray@google.com> | 2014-07-11 08:26:40 +0000 |
commit | 0025a86411145eb7cd4971f9234fc21c7b4aced1 (patch) | |
tree | 933b8b96ea970c23a7b3ce313c7c6d46f807dadd | |
parent | 7fb36ded9cd5b1d254b63b3091f35c1e6471b90e (diff) | |
download | art-0025a86411145eb7cd4971f9234fc21c7b4aced1.zip art-0025a86411145eb7cd4971f9234fc21c7b4aced1.tar.gz art-0025a86411145eb7cd4971f9234fc21c7b4aced1.tar.bz2 |
Revert "Revert "Revert "Add implicit null and stack checks for x86"""
Broke the build.
This reverts commit 7fb36ded9cd5b1d254b63b3091f35c1e6471b90e.
Change-Id: I9df0e7446ff0913a0e1276a558b2ccf6c8f4c949
38 files changed, 382 insertions, 684 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index dd04837..3a19c40 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -237,7 +237,7 @@ define define-art-gtest-rule-host .PHONY: $$(gtest_rule) $$(gtest_rule): $$(gtest_exe) $$(ART_GTEST_$(1)_HOST_DEPS) $(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_TEST_HOST_GTEST_$(file)_DEX)) $$(gtest_deps) - $(hide) ($$(call ART_TEST_SKIP,$$@) && LD_PRELOAD=libsigchain$$(ART_HOST_SHLIB_EXTENSION) $$< && $$(call ART_TEST_PASSED,$$@)) \ + $(hide) ($$(call ART_TEST_SKIP,$$@) && $$< && $$(call ART_TEST_PASSED,$$@)) \ || $$(call ART_TEST_FAILED,$$@) ART_TEST_HOST_GTEST$$($(2)ART_PHONY_TEST_HOST_SUFFIX)_RULES += $$(gtest_rule) diff --git a/build/Android.oat.mk b/build/Android.oat.mk index 10936a4..61a2cde 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -48,6 +48,11 @@ ifneq ($(HOST_PREFER_32_BIT),true) $(eval $(call create-core-oat-host-rules,2ND_)) endif +IMPLICIT_CHECKS_arm := null,stack +IMPLICIT_CHECKS_arm64 := none +IMPLICIT_CHECKS_x86 := none +IMPLICIT_CHECKS_x86_64 := none +IMPLICIT_CHECKS_mips := none define create-core-oat-target-rules $$($(1)TARGET_CORE_IMG_OUT): $$($(1)TARGET_CORE_DEX_FILES) $$(DEX2OATD_DEPENDENCY) @echo "target dex2oat: $$@ ($$?)" @@ -58,6 +63,7 @@ $$($(1)TARGET_CORE_IMG_OUT): $$($(1)TARGET_CORE_DEX_FILES) $$(DEX2OATD_DEPENDENC --oat-location=$$($(1)TARGET_CORE_OAT) --image=$$($(1)TARGET_CORE_IMG_OUT) \ --base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) --instruction-set=$$($(1)TARGET_ARCH) \ --instruction-set-features=$$($(1)TARGET_INSTRUCTION_SET_FEATURES) \ + --implicit-checks=$(IMPLICIT_CHECKS_$($(1)TARGET_ARCH)) \ --android-root=$$(PRODUCT_OUT)/system --include-patch-information # This "renaming" eases declaration in art/Android.mk diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index 9d4c6b7..04d6898 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -190,7 +190,7 @@ void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) { null_check_branch = nullptr; // No null check. } else { // If the null-check fails its handled by the slow-path to reduce exception related meta-data. - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { null_check_branch = OpCmpImmBranch(kCondEq, rs_r0, 0, NULL); } } @@ -261,7 +261,7 @@ void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { null_check_branch = nullptr; // No null check. } else { // If the null-check fails its handled by the slow-path to reduce exception related meta-data. - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { null_check_branch = OpCmpImmBranch(kCondEq, rs_r0, 0, NULL); } } @@ -362,7 +362,7 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { Thread::kStackOverflowSignalReservedBytes; bool large_frame = (static_cast<size_t>(frame_size_) > kStackOverflowReservedUsableBytes); if (!skip_overflow_check) { - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetExplicitStackOverflowChecks()) { if (!large_frame) { /* Load stack limit */ LockTemp(rs_r12); @@ -401,7 +401,7 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { const int spill_size = spill_count * 4; const int frame_size_without_spills = frame_size_ - spill_size; if (!skip_overflow_check) { - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetExplicitStackOverflowChecks()) { class StackOverflowSlowPath : public LIRSlowPath { public: StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, bool restore_lr, size_t sp_displace) diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc index d319f53..56dcbe5 100644 --- a/compiler/dex/quick/arm64/call_arm64.cc +++ b/compiler/dex/quick/arm64/call_arm64.cc @@ -202,7 +202,7 @@ void Arm64Mir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) { null_check_branch = nullptr; // No null check. } else { // If the null-check fails its handled by the slow-path to reduce exception related meta-data. - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { null_check_branch = OpCmpImmBranch(kCondEq, rs_x0, 0, NULL); } } @@ -250,7 +250,7 @@ void Arm64Mir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) { null_check_branch = nullptr; // No null check. } else { // If the null-check fails its handled by the slow-path to reduce exception related meta-data. - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { null_check_branch = OpCmpImmBranch(kCondEq, rs_x0, 0, NULL); } } @@ -338,7 +338,7 @@ void Arm64Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) const int frame_size_without_spills = frame_size_ - spill_size; if (!skip_overflow_check) { - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetExplicitStackOverflowChecks()) { if (!large_frame) { // Load stack limit LoadWordDisp(rs_xSELF, Thread::StackEndOffset<8>().Int32Value(), rs_x9); @@ -371,7 +371,7 @@ void Arm64Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) } if (!skip_overflow_check) { - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetExplicitStackOverflowChecks()) { class StackOverflowSlowPath: public LIRSlowPath { public: StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, size_t sp_displace) : diff --git a/compiler/dex/quick/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h index 01d6354..7d75da9 100644 --- a/compiler/dex/quick/arm64/codegen_arm64.h +++ b/compiler/dex/quick/arm64/codegen_arm64.h @@ -96,7 +96,7 @@ class Arm64Mir2Lir FINAL : public Mir2Lir { RegStorage r_src, OpSize size) OVERRIDE; void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) OVERRIDE; LIR* OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg, - int offset, int check_value, LIR* target, LIR** compare) OVERRIDE; + int offset, int check_value, LIR* target) OVERRIDE; // Required for target - register utilities. RegStorage TargetReg(SpecialTargetRegister reg) OVERRIDE; diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc index 6be66a2..e8f5cb9 100644 --- a/compiler/dex/quick/arm64/int_arm64.cc +++ b/compiler/dex/quick/arm64/int_arm64.cc @@ -163,8 +163,7 @@ LIR* Arm64Mir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_ LIR* Arm64Mir2Lir::OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg, int offset, int check_value, - LIR* target, LIR** compare) { - DCHECK(compare == nullptr); + LIR* target) { // It is possible that temp register is 64-bit. (ArgReg or RefReg) // Always compare 32-bit value no matter what temp_reg is. if (temp_reg.Is64Bit()) { diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc index 949295d..ef9dbdd 100644 --- a/compiler/dex/quick/arm64/target_arm64.cc +++ b/compiler/dex/quick/arm64/target_arm64.cc @@ -1190,7 +1190,7 @@ int Arm64Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); if (pcrLabel) { - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1), info->opt_flags); } else { *pcrLabel = nullptr; diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 95468d2..048aca3 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -1172,12 +1172,9 @@ bool Mir2Lir::BadOverlap(RegLocation rl_src, RegLocation rl_dest) { } LIR *Mir2Lir::OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg, - int offset, int check_value, LIR* target, LIR** compare) { + int offset, int check_value, LIR* target) { // Handle this for architectures that can't compare to memory. - LIR* inst = Load32Disp(base_reg, offset, temp_reg); - if (compare != nullptr) { - *compare = inst; - } + Load32Disp(base_reg, offset, temp_reg); LIR* branch = OpCmpImmBranch(cond, temp_reg, check_value, target); return branch; } diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index b559cf5..6dc019a 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -176,7 +176,7 @@ LIR* Mir2Lir::GenNullCheck(RegStorage reg) { /* Perform null-check on a register. */ LIR* Mir2Lir::GenNullCheck(RegStorage m_reg, int opt_flags) { - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { return GenExplicitNullCheck(m_reg, opt_flags); } return nullptr; @@ -191,17 +191,16 @@ LIR* Mir2Lir::GenExplicitNullCheck(RegStorage m_reg, int opt_flags) { } void Mir2Lir::MarkPossibleNullPointerException(int opt_flags) { - if (cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && (opt_flags & MIR_IGNORE_NULL_CHECK)) { return; } - // Insert after last instruction. MarkSafepointPC(last_lir_insn_); } } void Mir2Lir::MarkPossibleNullPointerExceptionAfter(int opt_flags, LIR* after) { - if (cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && (opt_flags & MIR_IGNORE_NULL_CHECK)) { return; } @@ -210,13 +209,13 @@ void Mir2Lir::MarkPossibleNullPointerExceptionAfter(int opt_flags, LIR* after) { } void Mir2Lir::MarkPossibleStackOverflowException() { - if (cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetExplicitStackOverflowChecks()) { MarkSafepointPC(last_lir_insn_); } } void Mir2Lir::ForceImplicitNullCheck(RegStorage reg, int opt_flags) { - if (cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && (opt_flags & MIR_IGNORE_NULL_CHECK)) { return; } @@ -623,7 +622,7 @@ void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, bool is_long_or_double, LockTemp(r_tmp); LIR* uninit_branch = OpCmpMemImmBranch(kCondLt, r_tmp, r_base, mirror::Class::StatusOffset().Int32Value(), - mirror::Class::kStatusInitialized, nullptr, nullptr); + mirror::Class::kStatusInitialized, NULL); LIR* cont = NewLIR0(kPseudoTargetLabel); AddSlowPath(new (arena_) StaticFieldSlowPath(this, unresolved_branch, uninit_branch, cont, @@ -716,7 +715,7 @@ void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest, LockTemp(r_tmp); LIR* uninit_branch = OpCmpMemImmBranch(kCondLt, r_tmp, r_base, mirror::Class::StatusOffset().Int32Value(), - mirror::Class::kStatusInitialized, nullptr, nullptr); + mirror::Class::kStatusInitialized, NULL); LIR* cont = NewLIR0(kPseudoTargetLabel); AddSlowPath(new (arena_) StaticFieldSlowPath(this, unresolved_branch, uninit_branch, cont, @@ -2190,7 +2189,7 @@ class SuspendCheckSlowPath : public Mir2Lir::LIRSlowPath { /* Check if we need to check for pending suspend request */ void Mir2Lir::GenSuspendTest(int opt_flags) { - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitSuspendChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetExplicitSuspendChecks()) { if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK)) { return; } @@ -2210,7 +2209,7 @@ void Mir2Lir::GenSuspendTest(int opt_flags) { /* Check if we need to check for pending suspend request */ void Mir2Lir::GenSuspendTestAndBranch(int opt_flags, LIR* target) { - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitSuspendChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetExplicitSuspendChecks()) { if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK)) { OpUnconditionalBranch(target); return; diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 1dbf2ea..6c0dfe8 100755 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -981,35 +981,21 @@ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, type, skip_this); if (pcrLabel) { - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { *pcrLabel = GenExplicitNullCheck(TargetRefReg(kArg1), info->opt_flags); } else { *pcrLabel = nullptr; - if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && - (info->opt_flags & MIR_IGNORE_NULL_CHECK)) { - return call_state; - } // In lieu of generating a check for kArg1 being null, we need to // perform a load when doing implicit checks. - GenImplicitNullCheck(TargetReg(kArg1, false), info->opt_flags); + RegStorage tmp = AllocTemp(); + Load32Disp(TargetRefReg(kArg1), 0, tmp); + MarkPossibleNullPointerException(info->opt_flags); + FreeTemp(tmp); } } return call_state; } -// Default implementation of implicit null pointer check. -// Overridden by arch specific as necessary. -void Mir2Lir::GenImplicitNullCheck(RegStorage reg, int opt_flags) { - if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && (opt_flags & MIR_IGNORE_NULL_CHECK)) { - return; - } - RegStorage tmp = AllocTemp(); - Load32Disp(reg, 0, tmp); - MarkPossibleNullPointerException(opt_flags); - FreeTemp(tmp); -} - - /* * May have 0+ arguments (also used for jumbo). Note that * source virtual registers may be in physical registers, so may @@ -1222,17 +1208,16 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); if (pcrLabel) { - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { *pcrLabel = GenExplicitNullCheck(TargetRefReg(kArg1), info->opt_flags); } else { *pcrLabel = nullptr; - if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && - (info->opt_flags & MIR_IGNORE_NULL_CHECK)) { - return call_state; - } // In lieu of generating a check for kArg1 being null, we need to // perform a load when doing implicit checks. - GenImplicitNullCheck(TargetReg(kArg1, false), info->opt_flags); + RegStorage tmp = AllocTemp(); + Load32Disp(TargetRefReg(kArg1), 0, tmp); + MarkPossibleNullPointerException(info->opt_flags); + FreeTemp(tmp); } } return call_state; @@ -1308,14 +1293,11 @@ bool Mir2Lir::GenInlinedCharAt(CallInfo* info) { // On x86, we can compare to memory directly // Set up a launch pad to allow retry in case of bounds violation */ if (rl_idx.is_const) { - LIR* comparison; range_check_branch = OpCmpMemImmBranch( kCondUlt, RegStorage::InvalidReg(), rl_obj.reg, count_offset, - mir_graph_->ConstantValue(rl_idx.orig_sreg), nullptr, &comparison); - MarkPossibleNullPointerExceptionAfter(0, comparison); - } else { + mir_graph_->ConstantValue(rl_idx.orig_sreg), nullptr); + } else { OpRegMem(kOpCmp, rl_idx.reg, rl_obj.reg, count_offset); - MarkPossibleNullPointerException(0); range_check_branch = OpCondBranch(kCondUge, nullptr); } } diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index e93c6e3..4885501 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -838,7 +838,6 @@ class Mir2Lir : public Backend { LIR* GenImmedCheck(ConditionCode c_code, RegStorage reg, int imm_val, ThrowKind kind); LIR* GenNullCheck(RegStorage m_reg, int opt_flags); LIR* GenExplicitNullCheck(RegStorage m_reg, int opt_flags); - virtual void GenImplicitNullCheck(RegStorage reg, int opt_flags); void GenCompareAndBranch(Instruction::Code opcode, RegLocation rl_src1, RegLocation rl_src2, LIR* taken, LIR* fall_through); void GenCompareZeroAndBranch(Instruction::Code opcode, RegLocation rl_src, @@ -1148,12 +1147,10 @@ class Mir2Lir : public Backend { * @param base_reg The register holding the base address. * @param offset The offset from the base. * @param check_value The immediate to compare to. - * @param target branch target (or nullptr) - * @param compare output for getting LIR for comparison (or nullptr) * @returns The branch instruction that was generated. */ virtual LIR* OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg, - int offset, int check_value, LIR* target, LIR** compare); + int offset, int check_value, LIR* target); // Required for target - codegen helpers. virtual bool SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index ebe3f0a..8df5b6d 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -271,22 +271,21 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { kX86Shrd64RRI, kRegRegImmStore, IS_TERTIARY_OP | REG_DEF0_USE01 | SETS_CCODES, { REX_W, 0, 0x0F, 0xAC, 0, 0, 0, 1, false }, "Shrd64RRI", "!0r,!1r,!2d" }, { kX86Shrd64MRI, kMemRegImm, IS_QUAD_OP | REG_USE02 | IS_LOAD | IS_STORE | SETS_CCODES, { REX_W, 0, 0x0F, 0xAC, 0, 0, 0, 1, false }, "Shrd64MRI", "[!0r+!1d],!2r,!3d" }, - { kX86Test8RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1, true }, "Test8RI", "!0r,!1d" }, - { kX86Test8MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1, true }, "Test8MI", "[!0r+!1d],!2d" }, - { kX86Test8AI, kArrayImm, IS_LOAD | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1, true }, "Test8AI", "[!0r+!1r<<!2d+!3d],!4d" }, - { kX86Test16RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { 0x66, 0, 0xF7, 0, 0, 0, 0, 2, false }, "Test16RI", "!0r,!1d" }, - { kX86Test16MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0x66, 0, 0xF7, 0, 0, 0, 0, 2, false }, "Test16MI", "[!0r+!1d],!2d" }, - { kX86Test16AI, kArrayImm, IS_LOAD | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { 0x66, 0, 0xF7, 0, 0, 0, 0, 2, false }, "Test16AI", "[!0r+!1r<<!2d+!3d],!4d" }, - { kX86Test32RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF7, 0, 0, 0, 0, 4, false }, "Test32RI", "!0r,!1d" }, - { kX86Test32MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF7, 0, 0, 0, 0, 4, false }, "Test32MI", "[!0r+!1d],!2d" }, - { kX86Test32AI, kArrayImm, IS_LOAD | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { 0, 0, 0xF7, 0, 0, 0, 0, 4, false }, "Test32AI", "[!0r+!1r<<!2d+!3d],!4d" }, + { kX86Test8RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1, true }, "Test8RI", "!0r,!1d" }, + { kX86Test8MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1, true }, "Test8MI", "[!0r+!1d],!2d" }, + { kX86Test8AI, kArrayImm, IS_LOAD | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1, true }, "Test8AI", "[!0r+!1r<<!2d+!3d],!4d" }, + { kX86Test16RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { 0x66, 0, 0xF7, 0, 0, 0, 0, 2, false }, "Test16RI", "!0r,!1d" }, + { kX86Test16MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0x66, 0, 0xF7, 0, 0, 0, 0, 2, false }, "Test16MI", "[!0r+!1d],!2d" }, + { kX86Test16AI, kArrayImm, IS_LOAD | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { 0x66, 0, 0xF7, 0, 0, 0, 0, 2, false }, "Test16AI", "[!0r+!1r<<!2d+!3d],!4d" }, + { kX86Test32RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF7, 0, 0, 0, 0, 4, false }, "Test32RI", "!0r,!1d" }, + { kX86Test32MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF7, 0, 0, 0, 0, 4, false }, "Test32MI", "[!0r+!1d],!2d" }, + { kX86Test32AI, kArrayImm, IS_LOAD | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { 0, 0, 0xF7, 0, 0, 0, 0, 4, false }, "Test32AI", "[!0r+!1r<<!2d+!3d],!4d" }, { kX86Test64RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { REX_W, 0, 0xF7, 0, 0, 0, 0, 4, false }, "Test64RI", "!0r,!1d" }, { kX86Test64MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { REX_W, 0, 0xF7, 0, 0, 0, 0, 4, false }, "Test64MI", "[!0r+!1d],!2d" }, { kX86Test64AI, kArrayImm, IS_LOAD | IS_QUIN_OP | REG_USE01 | SETS_CCODES, { REX_W, 0, 0xF7, 0, 0, 0, 0, 4, false }, "Test64AI", "[!0r+!1r<<!2d+!3d],!4d" }, - { kX86Test32RR, kRegReg, IS_BINARY_OP | REG_USE01 | SETS_CCODES, { 0, 0, 0x85, 0, 0, 0, 0, 0, false }, "Test32RR", "!0r,!1r" }, + { kX86Test32RR, kRegReg, IS_BINARY_OP | REG_USE01 | SETS_CCODES, { 0, 0, 0x85, 0, 0, 0, 0, 0, false }, "Test32RR", "!0r,!1r" }, { kX86Test64RR, kRegReg, IS_BINARY_OP | REG_USE01 | SETS_CCODES, { REX_W, 0, 0x85, 0, 0, 0, 0, 0, false }, "Test64RR", "!0r,!1r" }, - { kX86Test32RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0x85, 0, 0, 0, 0, 0, false }, "Test32RM", "!0r,[!1r+!1d]" }, #define UNARY_ENCODING_MAP(opname, modrm, is_store, sets_ccodes, \ reg, reg_kind, reg_flags, \ diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc index a5767a1..8e2a1e3 100644 --- a/compiler/dex/quick/x86/call_x86.cc +++ b/compiler/dex/quick/x86/call_x86.cc @@ -222,28 +222,15 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { LockTemp(rs_rX86_ARG1); LockTemp(rs_rX86_ARG2); + /* Build frame, return address already on stack */ + stack_decrement_ = OpRegImm(kOpSub, rs_rX86_SP, frame_size_ - GetInstructionSetPointerSize(cu_->instruction_set)); + /* * We can safely skip the stack overflow check if we're * a leaf *and* our frame size < fudge factor. */ - InstructionSet isa = cu_->target64 ? kX86_64 : kX86; - const bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !IsLargeFrame(frame_size_, isa); - - // If we doing an implicit stack overflow check, perform the load immediately - // before the stack pointer is decremented and anything is saved. - if (!skip_overflow_check && - cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { - // Implicit stack overflow check. - // test eax,[esp + -overflow] - int overflow = GetStackOverflowReservedBytes(isa); - NewLIR3(kX86Test32RM, rs_rAX.GetReg(), rs_rX86_SP.GetReg(), -overflow); - MarkPossibleStackOverflowException(); - } - - /* Build frame, return address already on stack */ - stack_decrement_ = OpRegImm(kOpSub, rs_rX86_SP, frame_size_ - - GetInstructionSetPointerSize(cu_->instruction_set)); - + const bool skip_overflow_check = mir_graph_->MethodIsLeaf() && + !IsLargeFrame(frame_size_, cu_->target64 ? kX86_64 : kX86); NewLIR0(kPseudoMethodEntry); /* Spill core callee saves */ SpillCoreRegs(); @@ -273,27 +260,25 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { private: const size_t sp_displace_; }; - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { - // TODO: for large frames we should do something like: - // spill ebp - // lea ebp, [esp + frame_size] - // cmp ebp, fs:[stack_end_] - // jcc stack_overflow_exception - // mov esp, ebp - // in case a signal comes in that's not using an alternate signal stack and the large frame - // may have moved us outside of the reserved area at the end of the stack. - // cmp rs_rX86_SP, fs:[stack_end_]; jcc throw_slowpath - if (cu_->target64) { - OpRegThreadMem(kOpCmp, rs_rX86_SP, Thread::StackEndOffset<8>()); - } else { - OpRegThreadMem(kOpCmp, rs_rX86_SP, Thread::StackEndOffset<4>()); - } - LIR* branch = OpCondBranch(kCondUlt, nullptr); - AddSlowPath( + // TODO: for large frames we should do something like: + // spill ebp + // lea ebp, [esp + frame_size] + // cmp ebp, fs:[stack_end_] + // jcc stack_overflow_exception + // mov esp, ebp + // in case a signal comes in that's not using an alternate signal stack and the large frame may + // have moved us outside of the reserved area at the end of the stack. + // cmp rs_rX86_SP, fs:[stack_end_]; jcc throw_slowpath + if (cu_->target64) { + OpRegThreadMem(kOpCmp, rs_rX86_SP, Thread::StackEndOffset<8>()); + } else { + OpRegThreadMem(kOpCmp, rs_rX86_SP, Thread::StackEndOffset<4>()); + } + LIR* branch = OpCondBranch(kCondUlt, nullptr); + AddSlowPath( new(arena_)StackOverflowSlowPath(this, branch, frame_size_ - GetInstructionSetPointerSize(cu_->instruction_set))); - } } FlushIns(ArgLocs, rl_method); @@ -333,14 +318,4 @@ void X86Mir2Lir::GenSpecialExitSequence() { NewLIR0(kX86Ret); } -void X86Mir2Lir::GenImplicitNullCheck(RegStorage reg, int opt_flags) { - if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && (opt_flags & MIR_IGNORE_NULL_CHECK)) { - return; - } - // Implicit null pointer check. - // test eax,[arg1+0] - NewLIR3(kX86Test32RM, rs_rAX.GetReg(), reg.GetReg(), 0); - MarkPossibleNullPointerException(opt_flags); -} - } // namespace art diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index e40d45e..b0c54e8 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -85,7 +85,6 @@ class X86Mir2Lir : public Mir2Lir { LIR* StoreBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int scale, int displacement, RegStorage r_src, OpSize size) OVERRIDE; void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg); - void GenImplicitNullCheck(RegStorage reg, int opt_flags); // Required for target - register utilities. RegStorage TargetReg(SpecialTargetRegister reg) OVERRIDE; @@ -799,11 +798,9 @@ class X86Mir2Lir : public Mir2Lir { * @param base_reg The register holding the base address. * @param offset The offset from the base. * @param check_value The immediate to compare to. - * @param target branch target (or nullptr) - * @param compare output for getting LIR for comparison (or nullptr) */ LIR* OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg, - int offset, int check_value, LIR* target, LIR** compare); + int offset, int check_value, LIR* target); /* * Can this operation be using core registers without temporaries? diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index e8118dc..f1166f6 100755 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -1092,7 +1092,6 @@ void X86Mir2Lir::GenArrayBoundsCheck(RegStorage index, }; OpRegMem(kOpCmp, index, array_base, len_offset); - MarkPossibleNullPointerException(0); LIR* branch = OpCondBranch(kCondUge, nullptr); AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch, index, array_base, len_offset)); @@ -1133,7 +1132,6 @@ void X86Mir2Lir::GenArrayBoundsCheck(int32_t index, }; NewLIR3(IS_SIMM8(index) ? kX86Cmp32MI8 : kX86Cmp32MI, array_base.GetReg(), len_offset, index); - MarkPossibleNullPointerException(0); LIR* branch = OpCondBranch(kCondLs, nullptr); AddSlowPath(new (arena_) ArrayBoundsCheckSlowPath(this, branch, index, array_base, len_offset)); diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index c283b40..1ebbbbd 100755 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -892,12 +892,8 @@ RegStorage X86Mir2Lir::LoadHelper(ThreadOffset<8> offset) { } LIR* X86Mir2Lir::CheckSuspendUsingLoad() { - // First load the pointer in fs:[suspend-trigger] into eax - // Then use a test instruction to indirect via that address. - NewLIR2(kX86Mov32RT, rs_rAX.GetReg(), cu_->target64 ? - Thread::ThreadSuspendTriggerOffset<8>().Int32Value() : - Thread::ThreadSuspendTriggerOffset<4>().Int32Value()); - return NewLIR3(kX86Test32RM, rs_rAX.GetReg(), rs_rAX.GetReg(), 0); + LOG(FATAL) << "Unexpected use of CheckSuspendUsingLoad in x86"; + return nullptr; } uint64_t X86Mir2Lir::GetTargetInstFlags(int opcode) { @@ -1260,7 +1256,6 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { // Is the string non-NULL? LoadValueDirectFixed(rl_obj, rs_rDX); GenNullCheck(rs_rDX, info->opt_flags); - // uint32_t opt_flags = info->opt_flags; info->opt_flags |= MIR_IGNORE_NULL_CHECK; // Record that we've null checked. // Does the character fit in 16 bits? @@ -1287,20 +1282,12 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { // Character is in EAX. // Object pointer is in EDX. - // Compute the number of words to search in to rCX. - Load32Disp(rs_rDX, count_offset, rs_rCX); - - // Possible signal here due to null pointer dereference. - // Note that the signal handler will expect the top word of - // the stack to be the ArtMethod*. If the PUSH edi instruction - // below is ahead of the load above then this will not be true - // and the signal handler will not work. - MarkPossibleNullPointerException(0); - // We need to preserve EDI, but have no spare registers, so push it on the stack. // We have to remember that all stack addresses after this are offset by sizeof(EDI). NewLIR1(kX86Push32R, rs_rDI.GetReg()); + // Compute the number of words to search in to rCX. + Load32Disp(rs_rDX, count_offset, rs_rCX); LIR *length_compare = nullptr; int start_value = 0; bool is_index_on_stack = false; @@ -2691,7 +2678,7 @@ int X86Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx, direct_code, direct_method, type); if (pcrLabel) { - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { *pcrLabel = GenExplicitNullCheck(TargetRefReg(kArg1), info->opt_flags); } else { *pcrLabel = nullptr; diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc index 952c700..5c7c91b 100644 --- a/compiler/dex/quick/x86/utility_x86.cc +++ b/compiler/dex/quick/x86/utility_x86.cc @@ -684,9 +684,9 @@ LIR* X86Mir2Lir::LoadBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int } else { DCHECK(!r_dest.IsFloat()); // Make sure we're not still using a pair here. if (r_base == r_dest.GetLow()) { - load = NewLIR3(opcode, r_dest.GetHighReg(), r_base.GetReg(), + load2 = NewLIR3(opcode, r_dest.GetHighReg(), r_base.GetReg(), displacement + HIWORD_OFFSET); - load2 = NewLIR3(opcode, r_dest.GetLowReg(), r_base.GetReg(), displacement + LOWORD_OFFSET); + load = NewLIR3(opcode, r_dest.GetLowReg(), r_base.GetReg(), displacement + LOWORD_OFFSET); } else { load = NewLIR3(opcode, r_dest.GetLowReg(), r_base.GetReg(), displacement + LOWORD_OFFSET); load2 = NewLIR3(opcode, r_dest.GetHighReg(), r_base.GetReg(), @@ -712,16 +712,16 @@ LIR* X86Mir2Lir::LoadBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int if (r_dest.GetHigh() == r_index) { // We can't use either register for the first load. RegStorage temp = AllocTemp(); - load = NewLIR5(opcode, temp.GetReg(), r_base.GetReg(), r_index.GetReg(), scale, + load2 = NewLIR5(opcode, temp.GetReg(), r_base.GetReg(), r_index.GetReg(), scale, displacement + HIWORD_OFFSET); - load2 = NewLIR5(opcode, r_dest.GetLowReg(), r_base.GetReg(), r_index.GetReg(), scale, + load = NewLIR5(opcode, r_dest.GetLowReg(), r_base.GetReg(), r_index.GetReg(), scale, displacement + LOWORD_OFFSET); OpRegCopy(r_dest.GetHigh(), temp); FreeTemp(temp); } else { - load = NewLIR5(opcode, r_dest.GetHighReg(), r_base.GetReg(), r_index.GetReg(), scale, + load2 = NewLIR5(opcode, r_dest.GetHighReg(), r_base.GetReg(), r_index.GetReg(), scale, displacement + HIWORD_OFFSET); - load2 = NewLIR5(opcode, r_dest.GetLowReg(), r_base.GetReg(), r_index.GetReg(), scale, + load = NewLIR5(opcode, r_dest.GetLowReg(), r_base.GetReg(), r_index.GetReg(), scale, displacement + LOWORD_OFFSET); } } else { @@ -744,7 +744,6 @@ LIR* X86Mir2Lir::LoadBaseIndexedDisp(RegStorage r_base, RegStorage r_index, int } } - // Always return first load generated as this might cause a fault if base is nullptr. return load; } @@ -882,12 +881,9 @@ LIR* X86Mir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r } LIR* X86Mir2Lir::OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg, - int offset, int check_value, LIR* target, LIR** compare) { - LIR* inst = NewLIR3(IS_SIMM8(check_value) ? kX86Cmp32MI8 : kX86Cmp32MI, base_reg.GetReg(), - offset, check_value); - if (compare != nullptr) { - *compare = inst; - } + int offset, int check_value, LIR* target) { + NewLIR3(IS_SIMM8(check_value) ? kX86Cmp32MI8 : kX86Cmp32MI, base_reg.GetReg(), offset, + check_value); LIR* branch = OpCondBranch(cond, target); return branch; } diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h index 17f9b91..5657381 100644 --- a/compiler/dex/quick/x86/x86_lir.h +++ b/compiler/dex/quick/x86/x86_lir.h @@ -499,7 +499,6 @@ enum X86OpCode { UnaryOpcode(kX86Test, RI, MI, AI), kX86Test32RR, kX86Test64RR, - kX86Test32RM, UnaryOpcode(kX86Not, R, M, A), UnaryOpcode(kX86Neg, R, M, A), UnaryOpcode(kX86Mul, DaR, DaM, DaA), diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index c0f91d1..92b2fee 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -56,9 +56,9 @@ class CompilerOptions { include_patch_information_(kDefaultIncludePatchInformation), top_k_profile_threshold_(kDefaultTopKProfileThreshold), include_debug_symbols_(kDefaultIncludeDebugSymbols), - implicit_null_checks_(false), - implicit_so_checks_(false), - implicit_suspend_checks_(false) + explicit_null_checks_(true), + explicit_so_checks_(true), + explicit_suspend_checks_(true) #ifdef ART_SEA_IR_MODE , sea_ir_mode_(false) #endif @@ -74,9 +74,9 @@ class CompilerOptions { bool include_patch_information, double top_k_profile_threshold, bool include_debug_symbols, - bool implicit_null_checks, - bool implicit_so_checks, - bool implicit_suspend_checks + bool explicit_null_checks, + bool explicit_so_checks, + bool explicit_suspend_checks #ifdef ART_SEA_IR_MODE , bool sea_ir_mode #endif @@ -91,9 +91,9 @@ class CompilerOptions { include_patch_information_(include_patch_information), top_k_profile_threshold_(top_k_profile_threshold), include_debug_symbols_(include_debug_symbols), - implicit_null_checks_(implicit_null_checks), - implicit_so_checks_(implicit_so_checks), - implicit_suspend_checks_(implicit_suspend_checks) + explicit_null_checks_(explicit_null_checks), + explicit_so_checks_(explicit_so_checks), + explicit_suspend_checks_(explicit_suspend_checks) #ifdef ART_SEA_IR_MODE , sea_ir_mode_(sea_ir_mode) #endif @@ -160,28 +160,28 @@ class CompilerOptions { return include_debug_symbols_; } - bool GetImplicitNullChecks() const { - return implicit_null_checks_; + bool GetExplicitNullChecks() const { + return explicit_null_checks_; } - void SetImplicitNullChecks(bool new_val) { - implicit_null_checks_ = new_val; + void SetExplicitNullChecks(bool new_val) { + explicit_null_checks_ = new_val; } - bool GetImplicitStackOverflowChecks() const { - return implicit_so_checks_; + bool GetExplicitStackOverflowChecks() const { + return explicit_so_checks_; } - void SetImplicitStackOverflowChecks(bool new_val) { - implicit_so_checks_ = new_val; + void SetExplicitStackOverflowChecks(bool new_val) { + explicit_so_checks_ = new_val; } - bool GetImplicitSuspendChecks() const { - return implicit_suspend_checks_; + bool GetExplicitSuspendChecks() const { + return explicit_suspend_checks_; } - void SetImplicitSuspendChecks(bool new_val) { - implicit_suspend_checks_ = new_val; + void SetExplicitSuspendChecks(bool new_val) { + explicit_suspend_checks_ = new_val; } #ifdef ART_SEA_IR_MODE @@ -208,9 +208,9 @@ class CompilerOptions { // When using a profile file only the top K% of the profiled samples will be compiled. double top_k_profile_threshold_; bool include_debug_symbols_; - bool implicit_null_checks_; - bool implicit_so_checks_; - bool implicit_suspend_checks_; + bool explicit_null_checks_; + bool explicit_so_checks_; + bool explicit_suspend_checks_; #ifdef ART_SEA_IR_MODE bool sea_ir_mode_; #endif diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index c64a60c..80e7724 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -733,6 +733,20 @@ void ParseDouble(const std::string& option, char after_char, *parsed_value = value; } +void CheckExplicitCheckOptions(InstructionSet isa, bool* explicit_null_checks, + bool* explicit_so_checks, bool* explicit_suspend_checks) { + switch (isa) { + case kArm: + case kThumb2: + break; // All checks implemented, leave as is. + + default: // No checks implemented, reset all to explicit checks. + *explicit_null_checks = true; + *explicit_so_checks = true; + *explicit_suspend_checks = true; + } +} + static int dex2oat(int argc, char** argv) { #if defined(__linux__) && defined(__arm__) int major, minor; @@ -816,10 +830,10 @@ static int dex2oat(int argc, char** argv) { bool watch_dog_enabled = !kIsTargetBuild; bool generate_gdb_information = kIsDebugBuild; - // Checks are all explicit until we know the architecture. - bool implicit_null_checks = false; - bool implicit_so_checks = false; - bool implicit_suspend_checks = false; + bool explicit_null_checks = true; + bool explicit_so_checks = true; + bool explicit_suspend_checks = true; + bool has_explicit_checks_options = false; for (int i = 0; i < argc; i++) { const StringPiece option(argv[i]); @@ -996,6 +1010,31 @@ static int dex2oat(int argc, char** argv) { } else if (option.starts_with("--dump-cfg-passes=")) { std::string dump_passes = option.substr(strlen("--dump-cfg-passes=")).data(); PassDriverMEOpts::SetDumpPassList(dump_passes); + } else if (option.starts_with("--implicit-checks=")) { + std::string checks = option.substr(strlen("--implicit-checks=")).data(); + std::vector<std::string> checkvec; + Split(checks, ',', checkvec); + for (auto& str : checkvec) { + std::string val = Trim(str); + if (val == "none") { + explicit_null_checks = true; + explicit_so_checks = true; + explicit_suspend_checks = true; + } else if (val == "null") { + explicit_null_checks = false; + } else if (val == "suspend") { + explicit_suspend_checks = false; + } else if (val == "stack") { + explicit_so_checks = false; + } else if (val == "all") { + explicit_null_checks = false; + explicit_so_checks = false; + explicit_suspend_checks = false; + } else { + Usage("--implicit-checks passed non-recognized value %s", val.c_str()); + } + } + has_explicit_checks_options = true; } else if (option == "--include-patch-information") { include_patch_information = true; explicit_include_patch_information = true; @@ -1128,25 +1167,14 @@ static int dex2oat(int argc, char** argv) { Usage("Unknown --compiler-filter value %s", compiler_filter_string); } + CheckExplicitCheckOptions(instruction_set, &explicit_null_checks, &explicit_so_checks, + &explicit_suspend_checks); + if (!explicit_include_patch_information) { include_patch_information = (compiler_kind == Compiler::kQuick && CompilerOptions::kDefaultIncludePatchInformation); } - // Set the compilation target's implicit checks options. - switch (instruction_set) { - case kArm: - case kThumb2: - case kX86: - implicit_null_checks = true; - implicit_so_checks = true; - break; - - default: - // Defaults are correct. - break; - } - CompilerOptions compiler_options(compiler_filter, huge_method_threshold, large_method_threshold, @@ -1157,9 +1185,9 @@ static int dex2oat(int argc, char** argv) { include_patch_information, top_k_profile_threshold, include_debug_symbols, - implicit_null_checks, - implicit_so_checks, - implicit_suspend_checks + explicit_null_checks, + explicit_so_checks, + explicit_suspend_checks #ifdef ART_SEA_IR_MODE , compiler_options.sea_ir_ = true; #endif @@ -1231,8 +1259,26 @@ static int dex2oat(int argc, char** argv) { } std::unique_ptr<Dex2Oat> dex2oat(p_dex2oat); - - + // TODO: Not sure whether it's a good idea to allow anything else but the runtime option in + // this case at all, as we'll have to throw away produced code for a mismatch. + if (!has_explicit_checks_options) { + bool cross_compiling = true; + switch (kRuntimeISA) { + case kArm: + case kThumb2: + cross_compiling = instruction_set != kArm && instruction_set != kThumb2; + break; + default: + cross_compiling = instruction_set != kRuntimeISA; + break; + } + if (!cross_compiling) { + Runtime* runtime = Runtime::Current(); + compiler_options.SetExplicitNullChecks(runtime->ExplicitNullChecks()); + compiler_options.SetExplicitStackOverflowChecks(runtime->ExplicitStackOverflowChecks()); + compiler_options.SetExplicitSuspendChecks(runtime->ExplicitSuspendChecks()); + } + } // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start, // give it away now so that we don't starve GC. diff --git a/runtime/Android.mk b/runtime/Android.mk index 3774b32..7f5cf0c 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -412,7 +412,6 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT LOCAL_STATIC_LIBRARIES := libziparchive libz else # host LOCAL_STATIC_LIBRARIES += libcutils libziparchive-host libz libutils - LOCAL_SHARED_LIBRARIES += libsigchain LOCAL_LDLIBS += -ldl -lpthread ifeq ($$(HOST_OS),linux) LOCAL_LDLIBS += -lrt diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc index e22c56e..2a82129 100644 --- a/runtime/arch/arm/fault_handler_arm.cc +++ b/runtime/arch/arm/fault_handler_arm.cc @@ -46,10 +46,9 @@ static uint32_t GetInstructionSize(uint8_t* pc) { return instr_size; } -void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context, - mirror::ArtMethod** out_method, +void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method, uintptr_t* out_return_pc, uintptr_t* out_sp) { - struct ucontext* uc = reinterpret_cast<struct ucontext*>(context); + struct ucontext *uc = (struct ucontext *)context; struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext); *out_sp = static_cast<uintptr_t>(sc->arm_sp); VLOG(signals) << "sp: " << *out_sp; @@ -115,7 +114,7 @@ bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) { uint32_t checkinst1 = 0xf8d90000 + Thread::ThreadSuspendTriggerOffset<4>().Int32Value(); uint16_t checkinst2 = 0x6800; - struct ucontext* uc = reinterpret_cast<struct ucontext*>(context); + struct ucontext *uc = (struct ucontext *)context; struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext); uint8_t* ptr2 = reinterpret_cast<uint8_t*>(sc->arm_pc); uint8_t* ptr1 = ptr2 - 4; @@ -179,7 +178,7 @@ bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) { // to the overflow region below the protected region. bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { - struct ucontext* uc = reinterpret_cast<struct ucontext*>(context); + struct ucontext *uc = (struct ucontext *)context; struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext); VLOG(signals) << "stack overflow handler with sp at " << std::hex << &uc; VLOG(signals) << "sigcontext: " << std::hex << sc; @@ -206,7 +205,7 @@ bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { } // We know this is a stack overflow. We need to move the sp to the overflow region - // that exists below the protected region. Determine the address of the next + // the exists below the protected region. Determine the address of the next // available valid address below the protected region. uintptr_t prevsp = sp; sp = pregion; diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc index 34eede6..74c3023 100644 --- a/runtime/arch/arm64/fault_handler_arm64.cc +++ b/runtime/arch/arm64/fault_handler_arm64.cc @@ -29,8 +29,7 @@ namespace art { -void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context, - mirror::ArtMethod** out_method, +void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method, uintptr_t* out_return_pc, uintptr_t* out_sp) { } diff --git a/runtime/arch/mips/fault_handler_mips.cc b/runtime/arch/mips/fault_handler_mips.cc index 5a64a69..1ecd7d9 100644 --- a/runtime/arch/mips/fault_handler_mips.cc +++ b/runtime/arch/mips/fault_handler_mips.cc @@ -29,8 +29,7 @@ namespace art { -void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context, - mirror::ArtMethod** out_method, +void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method, uintptr_t* out_return_pc, uintptr_t* out_sp) { } diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc index f62200a..7c1980e 100644 --- a/runtime/arch/x86/fault_handler_x86.cc +++ b/runtime/arch/x86/fault_handler_x86.cc @@ -21,10 +21,6 @@ #include "globals.h" #include "base/logging.h" #include "base/hex_dump.h" -#include "mirror/art_method.h" -#include "mirror/art_method-inl.h" -#include "thread.h" -#include "thread-inl.h" // @@ -33,294 +29,19 @@ namespace art { -extern "C" void art_quick_throw_null_pointer_exception(); -extern "C" void art_quick_throw_stack_overflow_from_signal(); -extern "C" void art_quick_test_suspend(); - -// From the x86 disassembler... -enum SegmentPrefix { - kCs = 0x2e, - kSs = 0x36, - kDs = 0x3e, - kEs = 0x26, - kFs = 0x64, - kGs = 0x65, -}; - -// Get the size of an instruction in bytes. -static uint32_t GetInstructionSize(uint8_t* pc) { - uint8_t* instruction_start = pc; - bool have_prefixes = true; - bool two_byte = false; - - // Skip all the prefixes. - do { - switch (*pc) { - // Group 1 - lock and repeat prefixes: - case 0xF0: - case 0xF2: - case 0xF3: - // Group 2 - segment override prefixes: - case kCs: - case kSs: - case kDs: - case kEs: - case kFs: - case kGs: - // Group 3 - operand size override: - case 0x66: - // Group 4 - address size override: - case 0x67: - break; - default: - have_prefixes = false; - break; - } - if (have_prefixes) { - pc++; - } - } while (have_prefixes); - -#if defined(__x86_64__) - // Skip REX is present. - if (*pc >= 0x40 && *pc <= 0x4F) { - ++pc; - } -#endif - - // Check for known instructions. - uint32_t known_length = 0; - switch (*pc) { - case 0x83: // cmp [r + v], b: 4 byte instruction - known_length = 4; - break; - } - - if (known_length > 0) { - VLOG(signals) << "known instruction with length " << known_length; - return known_length; - } - - // Unknown instruction, work out length. - - // Work out if we have a ModR/M byte. - uint8_t opcode = *pc++; - if (opcode == 0xf) { - two_byte = true; - opcode = *pc++; - } - - bool has_modrm = false; // Is ModR/M byte present? - uint8_t hi = opcode >> 4; // Opcode high nybble. - uint8_t lo = opcode & 0b1111; // Opcode low nybble. - - // From the Intel opcode tables. - if (two_byte) { - has_modrm = true; // TODO: all of these? - } else if (hi < 4) { - has_modrm = lo < 4 || (lo >= 8 && lo <= 0xb); - } else if (hi == 6) { - has_modrm = lo == 3 || lo == 9 || lo == 0xb; - } else if (hi == 8) { - has_modrm = lo != 0xd; - } else if (hi == 0xc) { - has_modrm = lo == 1 || lo == 2 || lo == 6 || lo == 7; - } else if (hi == 0xd) { - has_modrm = lo < 4; - } else if (hi == 0xf) { - has_modrm = lo == 6 || lo == 7; - } - - if (has_modrm) { - uint8_t modrm = *pc++; - uint8_t mod = (modrm >> 6) & 0b11; - uint8_t reg = (modrm >> 3) & 0b111; - switch (mod) { - case 0: - break; - case 1: - if (reg == 4) { - // SIB + 1 byte displacement. - pc += 2; - } else { - pc += 1; - } - break; - case 2: - // SIB + 4 byte displacement. - pc += 5; - break; - case 3: - break; - } - } - - VLOG(signals) << "calculated X86 instruction size is " << (pc - instruction_start); - return pc - instruction_start; -} - -void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context, - mirror::ArtMethod** out_method, +void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method, uintptr_t* out_return_pc, uintptr_t* out_sp) { - struct ucontext* uc = reinterpret_cast<struct ucontext*>(context); - *out_sp = static_cast<uintptr_t>(uc->uc_mcontext.gregs[REG_ESP]); - VLOG(signals) << "sp: " << std::hex << *out_sp; - if (*out_sp == 0) { - return; - } - - // In the case of a stack overflow, the stack is not valid and we can't - // get the method from the top of the stack. However it's in EAX. - uintptr_t* fault_addr = reinterpret_cast<uintptr_t*>(siginfo->si_addr); - uintptr_t* overflow_addr = reinterpret_cast<uintptr_t*>( - reinterpret_cast<uint8_t*>(*out_sp) - GetStackOverflowReservedBytes(kX86)); - if (overflow_addr == fault_addr) { - *out_method = reinterpret_cast<mirror::ArtMethod*>(uc->uc_mcontext.gregs[REG_EAX]); - } else { - // The method is at the top of the stack. - *out_method = reinterpret_cast<mirror::ArtMethod*>(reinterpret_cast<uintptr_t*>(*out_sp)[0]); - } - - uint8_t* pc = reinterpret_cast<uint8_t*>(uc->uc_mcontext.gregs[REG_EIP]); - VLOG(signals) << HexDump(pc, 32, true, "PC "); - - uint32_t instr_size = GetInstructionSize(pc); - *out_return_pc = reinterpret_cast<uintptr_t>(pc + instr_size); } bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) { - struct ucontext *uc = reinterpret_cast<struct ucontext*>(context); - uint8_t* pc = reinterpret_cast<uint8_t*>(uc->uc_mcontext.gregs[REG_EIP]); - uint8_t* sp = reinterpret_cast<uint8_t*>(uc->uc_mcontext.gregs[REG_ESP]); - - uint32_t instr_size = GetInstructionSize(pc); - // We need to arrange for the signal handler to return to the null pointer - // exception generator. The return address must be the address of the - // next instruction (this instruction + instruction size). The return address - // is on the stack at the top address of the current frame. - - // Push the return address onto the stack. - uint32_t retaddr = reinterpret_cast<uint32_t>(pc + instr_size); - uint32_t* next_sp = reinterpret_cast<uint32_t*>(sp - 4); - *next_sp = retaddr; - uc->uc_mcontext.gregs[REG_ESP] = reinterpret_cast<uint32_t>(next_sp); - - uc->uc_mcontext.gregs[REG_EIP] = - reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception); - VLOG(signals) << "Generating null pointer exception"; - return true; + return false; } -// A suspend check is done using the following instruction sequence: -// 0xf720f1df: 648B058C000000 mov eax, fs:[0x8c] ; suspend_trigger -// .. some intervening instructions. -// 0xf720f1e6: 8500 test eax, [eax] - -// The offset from fs is Thread::ThreadSuspendTriggerOffset(). -// To check for a suspend check, we examine the instructions that caused -// the fault. bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) { - // These are the instructions to check for. The first one is the mov eax, fs:[xxx] - // where xxx is the offset of the suspend trigger. - uint32_t trigger = Thread::ThreadSuspendTriggerOffset<4>().Int32Value(); - - VLOG(signals) << "Checking for suspension point"; - uint8_t checkinst1[] = {0x64, 0x8b, 0x05, static_cast<uint8_t>(trigger & 0xff), - static_cast<uint8_t>((trigger >> 8) & 0xff), 0, 0}; - uint8_t checkinst2[] = {0x85, 0x00}; - - struct ucontext *uc = reinterpret_cast<struct ucontext*>(context); - uint8_t* pc = reinterpret_cast<uint8_t*>(uc->uc_mcontext.gregs[REG_EIP]); - uint8_t* sp = reinterpret_cast<uint8_t*>(uc->uc_mcontext.gregs[REG_ESP]); - - if (pc[0] != checkinst2[0] || pc[1] != checkinst2[1]) { - // Second instruction is not correct (test eax,[eax]). - VLOG(signals) << "Not a suspension point"; - return false; - } - - // The first instruction can a little bit up the stream due to load hoisting - // in the compiler. - uint8_t* limit = pc - 100; // Compiler will hoist to a max of 20 instructions. - uint8_t* ptr = pc - sizeof(checkinst1); - bool found = false; - while (ptr > limit) { - if (memcmp(ptr, checkinst1, sizeof(checkinst1)) == 0) { - found = true; - break; - } - ptr -= 1; - } - - if (found) { - VLOG(signals) << "suspend check match"; - - // We need to arrange for the signal handler to return to the null pointer - // exception generator. The return address must be the address of the - // next instruction (this instruction + 2). The return address - // is on the stack at the top address of the current frame. - - // Push the return address onto the stack. - uint32_t retaddr = reinterpret_cast<uint32_t>(pc + 2); - uint32_t* next_sp = reinterpret_cast<uint32_t*>(sp - 4); - *next_sp = retaddr; - uc->uc_mcontext.gregs[REG_ESP] = reinterpret_cast<uint32_t>(next_sp); - - uc->uc_mcontext.gregs[REG_EIP] = reinterpret_cast<uintptr_t>(art_quick_test_suspend); - - // Now remove the suspend trigger that caused this fault. - Thread::Current()->RemoveSuspendTrigger(); - VLOG(signals) << "removed suspend trigger invoking test suspend"; - return true; - } - VLOG(signals) << "Not a suspend check match, first instruction mismatch"; return false; } -// The stack overflow check is done using the following instruction: -// test eax, [esp+ -xxx] -// where 'xxx' is the size of the overflow area. -// -// This is done before any frame is established in the method. The return -// address for the previous method is on the stack at ESP. - bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { - struct ucontext *uc = reinterpret_cast<struct ucontext*>(context); - uintptr_t sp = static_cast<uintptr_t>(uc->uc_mcontext.gregs[REG_ESP]); - - uintptr_t fault_addr = reinterpret_cast<uintptr_t>(info->si_addr); - VLOG(signals) << "fault_addr: " << std::hex << fault_addr; - VLOG(signals) << "checking for stack overflow, sp: " << std::hex << sp << - ", fault_addr: " << fault_addr; - - uintptr_t overflow_addr = sp - GetStackOverflowReservedBytes(kX86); - - Thread* self = Thread::Current(); - uintptr_t pregion = reinterpret_cast<uintptr_t>(self->GetStackEnd()) - - Thread::kStackOverflowProtectedSize; - - // Check that the fault address is the value expected for a stack overflow. - if (fault_addr != overflow_addr) { - VLOG(signals) << "Not a stack overflow"; - return false; - } - - // We know this is a stack overflow. We need to move the sp to the overflow region - // that exists below the protected region. Determine the address of the next - // available valid address below the protected region. - VLOG(signals) << "setting sp to overflow region at " << std::hex << pregion; - - // Since the compiler puts the implicit overflow - // check before the callee save instructions, the SP is already pointing to - // the previous frame. - - // Tell the stack overflow code where the new stack pointer should be. - uc->uc_mcontext.gregs[REG_EAX] = pregion; - - // Now arrange for the signal handler to return to art_quick_throw_stack_overflow_from_signal. - uc->uc_mcontext.gregs[REG_EIP] = reinterpret_cast<uintptr_t>( - art_quick_throw_stack_overflow_from_signal); - - return true; + return false; } } // namespace art diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 68f46ad..24b9e46 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -173,21 +173,6 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode */ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFromCode -// On entry to this function, EAX contains the ESP value for the overflow region. -DEFINE_FUNCTION art_quick_throw_stack_overflow_from_signal - // Here, the ESP is above the protected region. We need to create a - // callee save frame and then move ESP down to the overflow region. - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context - mov %esp, %ecx // get current stack pointer - mov %eax, %esp // move ESP to the overflow region. - PUSH ecx // pass SP - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() - CFI_ADJUST_CFA_OFFSET(4) - SETUP_GOT_NOSAVE // clobbers ebx (harmless here) - call PLT_SYMBOL(artThrowStackOverflowFromCode) // artThrowStackOverflowFromCode(Thread*, SP) - int3 // unreached -END_FUNCTION art_quick_throw_stack_overflow_from_signal - /* * Called by managed code, saves callee saves and then calls artThrowException * that will place a mock Method* at the bottom of the stack. Arg1 holds the exception. diff --git a/runtime/arch/x86_64/fault_handler_x86_64.cc b/runtime/arch/x86_64/fault_handler_x86_64.cc index 88ae7f3..233d3c7 100644 --- a/runtime/arch/x86_64/fault_handler_x86_64.cc +++ b/runtime/arch/x86_64/fault_handler_x86_64.cc @@ -29,8 +29,7 @@ namespace art { -void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context, - mirror::ArtMethod** out_method, +void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method, uintptr_t* out_return_pc, uintptr_t* out_sp) { } diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc index f99ce07..3112bc0 100644 --- a/runtime/fault_handler.cc +++ b/runtime/fault_handler.cc @@ -29,7 +29,9 @@ #include "mirror/object-inl.h" #include "object_utils.h" #include "scoped_thread_state_change.h" +#ifdef HAVE_ANDROID_OS #include "sigchain.h" +#endif #include "verify_object-inl.h" namespace art { @@ -45,7 +47,6 @@ void art_sigsegv_fault() { // Signal handler called on SIGSEGV. static void art_fault_handler(int sig, siginfo_t* info, void* context) { - // std::cout << "handling fault in ART handler\n"; fault_manager.HandleFault(sig, info, context); } @@ -54,7 +55,9 @@ FaultManager::FaultManager() { } FaultManager::~FaultManager() { +#ifdef HAVE_ANDROID_OS UnclaimSignalChain(SIGSEGV); +#endif sigaction(SIGSEGV, &oldaction_, nullptr); // Restore old handler. } @@ -69,12 +72,11 @@ void FaultManager::Init() { #endif // Set our signal handler now. - int e = sigaction(SIGSEGV, &action, &oldaction_); - if (e != 0) { - VLOG(signals) << "Failed to claim SEGV: " << strerror(errno); - } + sigaction(SIGSEGV, &action, &oldaction_); +#ifdef HAVE_ANDROID_OS // Make sure our signal handler is called before any user handlers. ClaimSignalChain(SIGSEGV, &oldaction_); +#endif } void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) { @@ -82,12 +84,8 @@ void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) { // // If malloc calls abort, it will be holding its lock. // If the handler tries to call malloc, it will deadlock. - - // Also, there is only an 8K stack available here to logging can cause memory - // overwrite issues if you are unlucky. If you want to enable logging and - // are getting crashes, allocate more space for the alternate signal stack. VLOG(signals) << "Handling fault"; - if (IsInGeneratedCode(info, context, true)) { + if (IsInGeneratedCode(context, true)) { VLOG(signals) << "in generated code, looking for handler"; for (const auto& handler : generated_code_handlers_) { VLOG(signals) << "invoking Action on handler " << handler; @@ -103,8 +101,11 @@ void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) { } art_sigsegv_fault(); - // Pass this on to the next handler in the chain, or the default if none. +#ifdef HAVE_ANDROID_OS InvokeUserSignalHandler(sig, info, context); +#else + oldaction_.sa_sigaction(sig, info, context); +#endif } void FaultManager::AddHandler(FaultHandler* handler, bool generated_code) { @@ -131,7 +132,7 @@ void FaultManager::RemoveHandler(FaultHandler* handler) { // This function is called within the signal handler. It checks that // the mutator_lock is held (shared). No annotalysis is done. -bool FaultManager::IsInGeneratedCode(siginfo_t* siginfo, void* context, bool check_dex_pc) { +bool FaultManager::IsInGeneratedCode(void* context, bool check_dex_pc) { // We can only be running Java code in the current thread if it // is in Runnable state. VLOG(signals) << "Checking for generated code"; @@ -160,7 +161,7 @@ bool FaultManager::IsInGeneratedCode(siginfo_t* siginfo, void* context, bool che // Get the architecture specific method address and return address. These // are in architecture specific files in arch/<arch>/fault_handler_<arch>. - GetMethodAndReturnPCAndSP(siginfo, context, &method_obj, &return_pc, &sp); + GetMethodAndReturnPCAndSP(context, &method_obj, &return_pc, &sp); // If we don't have a potential method, we're outta here. VLOG(signals) << "potential method: " << method_obj; @@ -241,12 +242,12 @@ JavaStackTraceHandler::JavaStackTraceHandler(FaultManager* manager) : FaultHandl bool JavaStackTraceHandler::Action(int sig, siginfo_t* siginfo, void* context) { // Make sure that we are in the generated code, but we may not have a dex pc. - if (manager_->IsInGeneratedCode(siginfo, context, false)) { + if (manager_->IsInGeneratedCode(context, false)) { LOG(ERROR) << "Dumping java stack trace for crash in generated code"; mirror::ArtMethod* method = nullptr; uintptr_t return_pc = 0; uintptr_t sp = 0; - manager_->GetMethodAndReturnPCAndSP(siginfo, context, &method, &return_pc, &sp); + manager_->GetMethodAndReturnPCAndSP(context, &method, &return_pc, &sp); Thread* self = Thread::Current(); // Inside of generated code, sp[0] is the method, so sp is the frame. StackReference<mirror::ArtMethod>* frame = diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h index 71c9977..026f5b9 100644 --- a/runtime/fault_handler.h +++ b/runtime/fault_handler.h @@ -43,10 +43,9 @@ class FaultManager { void HandleFault(int sig, siginfo_t* info, void* context); void AddHandler(FaultHandler* handler, bool generated_code); void RemoveHandler(FaultHandler* handler); - void GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context, mirror::ArtMethod** out_method, + void GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method, uintptr_t* out_return_pc, uintptr_t* out_sp); - bool IsInGeneratedCode(siginfo_t* siginfo, void *context, bool check_dex_pc) - NO_THREAD_SAFETY_ANALYSIS; + bool IsInGeneratedCode(void *context, bool check_dex_pc) NO_THREAD_SAFETY_ANALYSIS; private: std::vector<FaultHandler*> generated_code_handlers_; diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index a5ade3e..e1e133f 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -258,6 +258,38 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni verify_ = true; image_isa_ = kRuntimeISA; + // Default to explicit checks. Switch off with -implicit-checks:. + // or setprop dalvik.vm.implicit_checks check1,check2,... +#ifdef HAVE_ANDROID_OS + { + char buf[PROP_VALUE_MAX]; + property_get("dalvik.vm.implicit_checks", buf, "null,stack"); + std::string checks(buf); + std::vector<std::string> checkvec; + Split(checks, ',', checkvec); + explicit_checks_ = kExplicitNullCheck | kExplicitSuspendCheck | + kExplicitStackOverflowCheck; + for (auto& str : checkvec) { + std::string val = Trim(str); + if (val == "none") { + explicit_checks_ = kExplicitNullCheck | kExplicitSuspendCheck | + kExplicitStackOverflowCheck; + } else if (val == "null") { + explicit_checks_ &= ~kExplicitNullCheck; + } else if (val == "suspend") { + explicit_checks_ &= ~kExplicitSuspendCheck; + } else if (val == "stack") { + explicit_checks_ &= ~kExplicitStackOverflowCheck; + } else if (val == "all") { + explicit_checks_ = 0; + } + } + } +#else + explicit_checks_ = kExplicitNullCheck | kExplicitSuspendCheck | + kExplicitStackOverflowCheck; +#endif + for (size_t i = 0; i < options.size(); ++i) { if (true && options[0].first == "-Xzygote") { LOG(INFO) << "option[" << i << "]=" << options[i].first; @@ -273,7 +305,6 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni Exit(0); } else if (StartsWith(option, "-Xbootclasspath:")) { boot_class_path_string_ = option.substr(strlen("-Xbootclasspath:")).data(); - LOG(INFO) << "setting boot class path to " << boot_class_path_string_; } else if (option == "-classpath" || option == "-cp") { // TODO: support -Djava.class.path i++; @@ -542,6 +573,54 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni if (!ParseUnsignedInteger(option, ':', &profiler_options_.max_stack_depth_)) { return false; } + } else if (StartsWith(option, "-implicit-checks:")) { + std::string checks; + if (!ParseStringAfterChar(option, ':', &checks)) { + return false; + } + std::vector<std::string> checkvec; + Split(checks, ',', checkvec); + for (auto& str : checkvec) { + std::string val = Trim(str); + if (val == "none") { + explicit_checks_ = kExplicitNullCheck | kExplicitSuspendCheck | + kExplicitStackOverflowCheck; + } else if (val == "null") { + explicit_checks_ &= ~kExplicitNullCheck; + } else if (val == "suspend") { + explicit_checks_ &= ~kExplicitSuspendCheck; + } else if (val == "stack") { + explicit_checks_ &= ~kExplicitStackOverflowCheck; + } else if (val == "all") { + explicit_checks_ = 0; + } else { + return false; + } + } + } else if (StartsWith(option, "-explicit-checks:")) { + std::string checks; + if (!ParseStringAfterChar(option, ':', &checks)) { + return false; + } + std::vector<std::string> checkvec; + Split(checks, ',', checkvec); + for (auto& str : checkvec) { + std::string val = Trim(str); + if (val == "none") { + explicit_checks_ = 0; + } else if (val == "null") { + explicit_checks_ |= kExplicitNullCheck; + } else if (val == "suspend") { + explicit_checks_ |= kExplicitSuspendCheck; + } else if (val == "stack") { + explicit_checks_ |= kExplicitStackOverflowCheck; + } else if (val == "all") { + explicit_checks_ = kExplicitNullCheck | kExplicitSuspendCheck | + kExplicitStackOverflowCheck; + } else { + return false; + } + } } else if (StartsWith(option, "-Xcompiler:")) { if (!ParseStringAfterChar(option, ':', &compiler_executable_)) { return false; diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h index fe719b1..d0f3c12 100644 --- a/runtime/parsed_options.h +++ b/runtime/parsed_options.h @@ -84,6 +84,11 @@ class ParsedOptions { bool verify_; InstructionSet image_isa_; + static constexpr uint32_t kExplicitNullCheck = 1; + static constexpr uint32_t kExplicitSuspendCheck = 2; + static constexpr uint32_t kExplicitStackOverflowCheck = 4; + uint32_t explicit_checks_; + private: ParsedOptions() {} diff --git a/runtime/runtime.cc b/runtime/runtime.cc index f165ffa..efa205e 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -140,10 +140,7 @@ Runtime::Runtime() suspend_handler_(nullptr), stack_overflow_handler_(nullptr), verify_(false), - target_sdk_version_(0), - implicit_null_checks_(false), - implicit_so_checks_(false), - implicit_suspend_checks_(false) { + target_sdk_version_(0) { for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { callee_save_methods_[i] = nullptr; } @@ -583,6 +580,41 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { GetInstrumentation()->ForceInterpretOnly(); } + bool implicit_checks_supported = false; + switch (kRuntimeISA) { + case kArm: + case kThumb2: + implicit_checks_supported = true; + break; + default: + break; + } + + if (!options->interpreter_only_ && implicit_checks_supported && + (options->explicit_checks_ != (ParsedOptions::kExplicitSuspendCheck | + ParsedOptions::kExplicitNullCheck | + ParsedOptions::kExplicitStackOverflowCheck) || kEnableJavaStackTraceHandler)) { + fault_manager.Init(); + + // These need to be in a specific order. The null point check handler must be + // after the suspend check and stack overflow check handlers. + if ((options->explicit_checks_ & ParsedOptions::kExplicitSuspendCheck) == 0) { + suspend_handler_ = new SuspensionHandler(&fault_manager); + } + + if ((options->explicit_checks_ & ParsedOptions::kExplicitStackOverflowCheck) == 0) { + stack_overflow_handler_ = new StackOverflowHandler(&fault_manager); + } + + if ((options->explicit_checks_ & ParsedOptions::kExplicitNullCheck) == 0) { + null_pointer_handler_ = new NullPointerHandler(&fault_manager); + } + + if (kEnableJavaStackTraceHandler) { + new JavaStackTraceHandler(&fault_manager); + } + } + heap_ = new gc::Heap(options->heap_initial_size_, options->heap_growth_limit_, options->heap_min_free_, @@ -613,42 +645,6 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { BlockSignals(); InitPlatformSignalHandlers(); - // Change the implicit checks flags based on runtime architecture. - switch (kRuntimeISA) { - case kArm: - case kThumb2: - case kX86: - implicit_null_checks_ = true; - implicit_so_checks_ = true; - break; - default: - // Keep the defaults. - break; - } - - if (!options->interpreter_only_ && - (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 - // after the suspend check and stack overflow check handlers. - if (implicit_suspend_checks_) { - suspend_handler_ = new SuspensionHandler(&fault_manager); - } - - if (implicit_so_checks_) { - stack_overflow_handler_ = new StackOverflowHandler(&fault_manager); - } - - if (implicit_null_checks_) { - null_pointer_handler_ = new NullPointerHandler(&fault_manager); - } - - if (kEnableJavaStackTraceHandler) { - new JavaStackTraceHandler(&fault_manager); - } - } - java_vm_ = new JavaVMExt(this, options.get()); Thread::Startup(); @@ -1220,6 +1216,37 @@ void Runtime::AddCurrentRuntimeFeaturesAsDex2OatArguments(std::vector<std::strin argv->push_back("--compiler-filter=interpret-only"); } + argv->push_back("--runtime-arg"); + std::string checkstr = "-implicit-checks"; + + int nchecks = 0; + char checksep = ':'; + + if (!ExplicitNullChecks()) { + checkstr += checksep; + checksep = ','; + checkstr += "null"; + ++nchecks; + } + if (!ExplicitSuspendChecks()) { + checkstr += checksep; + checksep = ','; + checkstr += "suspend"; + ++nchecks; + } + + if (!ExplicitStackOverflowChecks()) { + checkstr += checksep; + checksep = ','; + checkstr += "stack"; + ++nchecks; + } + + if (nchecks == 0) { + checkstr += ":none"; + } + argv->push_back(checkstr); + // Make the dex2oat instruction set match that of the launching runtime. If we have multiple // architecture support, dex2oat may be compiled as a different instruction-set than that // currently being executed. diff --git a/runtime/runtime.h b/runtime/runtime.h index 28f1217..f839be1 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -596,11 +596,6 @@ class Runtime { // Specifies target SDK version to allow workarounds for certain API levels. int32_t target_sdk_version_; - // Implicit checks flags. - bool implicit_null_checks_; // NullPointer checks are implicit. - bool implicit_so_checks_; // StackOverflow checks are implicit. - bool implicit_suspend_checks_; // Thread suspension checks are implicit. - DISALLOW_COPY_AND_ASSIGN(Runtime); }; diff --git a/runtime/thread.cc b/runtime/thread.cc index e5ccde5..7827dfb 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -231,99 +231,47 @@ static size_t FixStackSize(size_t stack_size) { return stack_size; } -// Global variable to prevent the compiler optimizing away the page reads for the stack. -byte dont_optimize_this; - // Install a protected region in the stack. This is used to trigger a SIGSEGV if a stack // overflow is detected. It is located right below the stack_end_. Just below that // is the StackOverflow reserved region used when creating the StackOverflow // exception. -// -// There is a little complexity here that deserves a special mention. When running on the -// host (glibc), the process's main thread's stack is allocated with a special flag -// to prevent memory being allocated when it's not needed. This flag makes the -// kernel only allocate memory for the stack by growing down in memory. Because we -// want to put an mprotected region far away from that at the stack top, we need -// to make sure the pages for the stack are mapped in before we call mprotect. We do -// this by reading every page from the stack bottom (highest address) to the stack top. -// We then madvise this away. void Thread::InstallImplicitProtection(bool is_main_stack) { byte* pregion = tlsPtr_.stack_end; - byte* stack_lowmem = tlsPtr_.stack_begin; - byte* stack_top = reinterpret_cast<byte*>(reinterpret_cast<uintptr_t>(&pregion) & - ~(kPageSize - 1)); // Page containing current top of stack. -#ifndef HAVE_ANDROID_OS - bool running_on_host = true; -#else - bool running_on_host = false; -#endif - - if (running_on_host) { - // On Host, we need to map in the main stack. This must be done by reading from the - // current stack pointer downwards as the stack is mapped using VM_GROWSDOWN - // in the kernel. Any access more than a page below the current SP will cause - // a segv. - if (is_main_stack) { - // First we need to unprotect the protected region because this may - // be called more than once for a particular stack and we will crash - // if we try to read the protected page. - mprotect(pregion - kStackOverflowProtectedSize, kStackOverflowProtectedSize, PROT_READ); - - // Read every page from the high address to the low. - for (byte* p = stack_top; p > stack_lowmem; p -= kPageSize) { - dont_optimize_this = *p; - } - } - } - - // Check and place a marker word at the lowest usable address in the stack. This - // is used to prevent a double protection. constexpr uint32_t kMarker = 0xdadadada; uintptr_t *marker = reinterpret_cast<uintptr_t*>(pregion); if (*marker == kMarker) { - // The region has already been set up. But on the main stack on the host we have - // removed the protected region in order to read the stack memory. We need to put - // this back again. - if (is_main_stack && running_on_host) { - mprotect(pregion - kStackOverflowProtectedSize, kStackOverflowProtectedSize, PROT_NONE); - madvise(stack_lowmem, stack_top - stack_lowmem, MADV_DONTNEED); - } + // The region has already been set up. return; } // Add marker so that we can detect a second attempt to do this. *marker = kMarker; - if (!running_on_host) { - // Running on Android, stacks are mapped cleanly. The protected region for the - // main stack just needs to be mapped in. We do this by writing one byte per page. - for (byte* p = pregion - kStackOverflowProtectedSize; p < pregion; p += kPageSize) { - *p = 0; + pregion -= kStackOverflowProtectedSize; + + // Touch the pages in the region to map them in. Otherwise mprotect fails. Only + // need to do this on the main stack. We only need to touch one byte per page. + if (is_main_stack) { + byte* start = pregion; + byte* end = pregion + kStackOverflowProtectedSize; + while (start < end) { + *start = static_cast<byte>(0); + start += kPageSize; } } - pregion -= kStackOverflowProtectedSize; - VLOG(threads) << "installing stack protected region at " << std::hex << static_cast<void*>(pregion) << " to " << static_cast<void*>(pregion + kStackOverflowProtectedSize - 1); - if (mprotect(pregion, kStackOverflowProtectedSize, PROT_NONE) == -1) { LOG(FATAL) << "Unable to create protected region in stack for implicit overflow check. Reason:" << strerror(errno); } // Tell the kernel that we won't be needing these pages any more. - // NB. madvise will probably write zeroes into the memory (on linux it does). if (is_main_stack) { - if (running_on_host) { - // On the host, it's the whole stack (minus a page to prevent overwrite of stack top). - madvise(stack_lowmem, stack_top - stack_lowmem - kPageSize, MADV_DONTNEED); - } else { - // On Android, just the protected region. - madvise(pregion, kStackOverflowProtectedSize, MADV_DONTNEED); - } + madvise(pregion, kStackOverflowProtectedSize, MADV_DONTNEED); } } @@ -584,17 +532,13 @@ void Thread::InitStackHwm() { // Install the protected region if we are doing implicit overflow checks. if (implicit_stack_check) { if (is_main_thread) { - size_t guardsize; - pthread_attr_t attributes; - CHECK_PTHREAD_CALL(pthread_attr_init, (&attributes), "guard size query"); - CHECK_PTHREAD_CALL(pthread_attr_getguardsize, (&attributes, &guardsize), "guard size query"); - CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attributes), "guard size query"); - // The main thread might have protected region at the bottom. We need + // The main thread has a 16K protected region at the bottom. We need // to install our own region so we need to move the limits // of the stack to make room for it. - tlsPtr_.stack_begin += guardsize; - tlsPtr_.stack_end += guardsize; - tlsPtr_.stack_size -= guardsize; + constexpr uint32_t kDelta = 16 * KB; + tlsPtr_.stack_begin += kDelta; + tlsPtr_.stack_end += kDelta; + tlsPtr_.stack_size -= kDelta; } InstallImplicitProtection(is_main_thread); } diff --git a/runtime/thread_linux.cc b/runtime/thread_linux.cc index 518211b..ee66ccc 100644 --- a/runtime/thread_linux.cc +++ b/runtime/thread_linux.cc @@ -35,8 +35,8 @@ static void SigAltStack(stack_t* new_stack, stack_t* old_stack) { void Thread::SetUpAlternateSignalStack() { // Create and set an alternate signal stack. stack_t ss; - ss.ss_sp = new uint8_t[SIGSTKSZ * 2]; // NB. this is 16K. - ss.ss_size = SIGSTKSZ * 2; + ss.ss_sp = new uint8_t[SIGSTKSZ]; + ss.ss_size = SIGSTKSZ; ss.ss_flags = 0; CHECK(ss.ss_sp != NULL); SigAltStack(&ss, NULL); @@ -56,7 +56,7 @@ void Thread::TearDownAlternateSignalStack() { // Tell the kernel to stop using it. ss.ss_sp = NULL; ss.ss_flags = SS_DISABLE; - ss.ss_size = SIGSTKSZ * 2; // Avoid ENOMEM failure with Mac OS' buggy libc. + ss.ss_size = SIGSTKSZ; // Avoid ENOMEM failure with Mac OS' buggy libc. SigAltStack(&ss, NULL); // Free it. diff --git a/sigchainlib/Android.mk b/sigchainlib/Android.mk index 20c8cac..8e25339 100644 --- a/sigchainlib/Android.mk +++ b/sigchainlib/Android.mk @@ -28,16 +28,3 @@ LOCAL_SHARED_LIBRARIES := liblog libdl LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.common_build.mk include $(BUILD_SHARED_LIBRARY) - -# Build host library. -include $(CLEAR_VARS) -LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) -LOCAL_MODULE_TAGS := optional -LOCAL_IS_HOST_MODULE := true -LOCAL_CFLAGS += $(ART_HOST_CFLAGS) -LOCAL_SRC_FILES := sigchain.cc -LOCAL_MODULE:= libsigchain -LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk -LOCAL_LDLIBS = -ldl -LOCAL_MULTILIB := both -include $(BUILD_HOST_SHARED_LIBRARY) diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc index 458ad69..5a5805f 100644 --- a/sigchainlib/sigchain.cc +++ b/sigchainlib/sigchain.cc @@ -14,13 +14,7 @@ * limitations under the License. */ -#ifdef HAVE_ANDROID_OS #include <android/log.h> -#else -#include <stdarg.h> -#include <iostream> -#endif - #include <dlfcn.h> #include <signal.h> #include <stdio.h> @@ -73,11 +67,7 @@ static void log(const char* format, ...) { va_list ap; va_start(ap, format); vsnprintf(buf, sizeof(buf), format, ap); -#ifdef HAVE_ANDROID_OS __android_log_write(ANDROID_LOG_ERROR, "libsigchain", buf); -#else - std::cout << buf << "\n"; -#endif va_end(ap); } @@ -114,16 +104,10 @@ void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) { if ((action.sa_flags & SA_SIGINFO) == 0) { if (action.sa_handler != NULL) { action.sa_handler(sig); - } else { - signal(sig, SIG_DFL); - raise(sig); } } else { if (action.sa_sigaction != NULL) { action.sa_sigaction(sig, info, context); - } else { - signal(sig, SIG_DFL); - raise(sig); } } } diff --git a/test/Android.oat.mk b/test/Android.oat.mk index 2b142db..16300bb 100644 --- a/test/Android.oat.mk +++ b/test/Android.oat.mk @@ -203,7 +203,6 @@ $(3): $$(ART_TEST_HOST_OAT_$(1)_DEX) $(ART_TEST_HOST_OAT_DEPENDENCIES) ANDROID_ROOT=$(HOST_OUT) \ ANDROID_LOG_TAGS='*:d' \ LD_LIBRARY_PATH=$$($(2)ART_HOST_OUT_SHARED_LIBRARIES) \ - LD_PRELOAD=libsigchain$$(ART_HOST_SHLIB_EXTENSION) \ $(HOST_OUT_EXECUTABLES)/dalvikvm$$($(2)ART_PHONY_TEST_HOST_SUFFIX) $(DALVIKVM_FLAGS) $(5) \ -XXlib:libartd$(HOST_SHLIB_SUFFIX) -Ximage:$$(HOST_CORE_IMG_LOCATION) \ -classpath $(ART_HOST_TEST_DIR)/android-data-$$@/oat-test-dex-$(1).jar \ |