diff options
author | Dave Allison <dallison@google.com> | 2014-07-11 17:11:58 +0000 |
---|---|---|
committer | Dave Allison <dallison@google.com> | 2014-07-16 14:58:27 -0700 |
commit | 69dfe51b684dd9d510dbcb63295fe180f998efde (patch) | |
tree | daa2522650ca03417e4518dc8aef989ec53a6065 | |
parent | 479f131d4bd3829dd512312020808b05f5a591f1 (diff) | |
download | art-69dfe51b684dd9d510dbcb63295fe180f998efde.tar.gz art-69dfe51b684dd9d510dbcb63295fe180f998efde.tar.bz2 art-69dfe51b684dd9d510dbcb63295fe180f998efde.zip |
Revert "Revert "Revert "Revert "Add implicit null and stack checks for x86""""
This reverts commit 0025a86411145eb7cd4971f9234fc21c7b4aced1.
Bug: 16256184
Change-Id: Ie0760a0c293aa3b62e2885398a8c512b7a946a73
41 files changed, 691 insertions, 595 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index a34058864c..ee51fcd92e 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -273,7 +273,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,$$@) && $$< && $$(call ART_TEST_PASSED,$$@)) \ + $(hide) ($$(call ART_TEST_SKIP,$$@) && LD_PRELOAD=libsigchain$$(ART_HOST_SHLIB_EXTENSION) $$< && $$(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 61a2cde534..10936a45d6 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -48,11 +48,6 @@ 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: $$@ ($$?)" @@ -63,7 +58,6 @@ $$($(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 01e17bf44f..6b96e929aa 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().GetExplicitNullChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { 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().GetExplicitNullChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { 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().GetExplicitStackOverflowChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { 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().GetExplicitStackOverflowChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { 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 8117c62954..d946ee39ef 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().GetExplicitNullChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { 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().GetExplicitNullChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { 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().GetExplicitStackOverflowChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { 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().GetExplicitStackOverflowChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { 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 de976531c2..246b682a5d 100644 --- a/compiler/dex/quick/arm64/codegen_arm64.h +++ b/compiler/dex/quick/arm64/codegen_arm64.h @@ -100,7 +100,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) OVERRIDE; + int offset, int check_value, LIR* target, LIR** compare) 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 6dc4a7ab51..2b78e81f46 100644 --- a/compiler/dex/quick/arm64/int_arm64.cc +++ b/compiler/dex/quick/arm64/int_arm64.cc @@ -296,7 +296,8 @@ 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* target, LIR** compare) { + DCHECK(compare == nullptr); // 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 6a27ad0b14..19a3cf1a3c 100644 --- a/compiler/dex/quick/arm64/target_arm64.cc +++ b/compiler/dex/quick/arm64/target_arm64.cc @@ -1195,7 +1195,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().GetExplicitNullChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags); } else { *pcrLabel = nullptr; diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 60d25890d4..463f277e54 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -1172,9 +1172,12 @@ 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) { + int offset, int check_value, LIR* target, LIR** compare) { // Handle this for architectures that can't compare to memory. - Load32Disp(base_reg, offset, temp_reg); + LIR* inst = Load32Disp(base_reg, offset, temp_reg); + if (compare != nullptr) { + *compare = inst; + } 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 1fc0cff678..cf80ee71a4 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().GetExplicitNullChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { return GenExplicitNullCheck(m_reg, opt_flags); } return nullptr; @@ -191,16 +191,17 @@ LIR* Mir2Lir::GenExplicitNullCheck(RegStorage m_reg, int opt_flags) { } void Mir2Lir::MarkPossibleNullPointerException(int opt_flags) { - if (!cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { 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().GetExplicitNullChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && (opt_flags & MIR_IGNORE_NULL_CHECK)) { return; } @@ -209,13 +210,13 @@ void Mir2Lir::MarkPossibleNullPointerExceptionAfter(int opt_flags, LIR* after) { } void Mir2Lir::MarkPossibleStackOverflowException() { - if (!cu_->compiler_driver->GetCompilerOptions().GetExplicitStackOverflowChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { MarkSafepointPC(last_lir_insn_); } } void Mir2Lir::ForceImplicitNullCheck(RegStorage reg, int opt_flags) { - if (!cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { + if (cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && (opt_flags & MIR_IGNORE_NULL_CHECK)) { return; } @@ -623,7 +624,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, NULL); + mirror::Class::kStatusInitialized, nullptr, nullptr); LIR* cont = NewLIR0(kPseudoTargetLabel); AddSlowPath(new (arena_) StaticFieldSlowPath(this, unresolved_branch, uninit_branch, cont, @@ -720,7 +721,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, NULL); + mirror::Class::kStatusInitialized, nullptr, nullptr); LIR* cont = NewLIR0(kPseudoTargetLabel); AddSlowPath(new (arena_) StaticFieldSlowPath(this, unresolved_branch, uninit_branch, cont, @@ -2198,7 +2199,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().GetExplicitSuspendChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitSuspendChecks()) { if (NO_SUSPEND || (opt_flags & MIR_IGNORE_SUSPEND_CHECK)) { return; } @@ -2218,7 +2219,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().GetExplicitSuspendChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitSuspendChecks()) { 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 9dedeae071..8ce6e1a206 100755 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -957,21 +957,35 @@ int Mir2Lir::GenDalvikArgsNoRange(CallInfo* info, type, skip_this); if (pcrLabel) { - if (cu_->compiler_driver->GetCompilerOptions().GetExplicitNullChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), 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. - RegStorage tmp = AllocTemp(); - Load32Disp(TargetReg(kArg1, kRef), 0, tmp); - MarkPossibleNullPointerException(info->opt_flags); - FreeTemp(tmp); + GenImplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags); } } 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 @@ -1186,16 +1200,17 @@ 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().GetExplicitNullChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), 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. - RegStorage tmp = AllocTemp(); - Load32Disp(TargetReg(kArg1, kRef), 0, tmp); - MarkPossibleNullPointerException(info->opt_flags); - FreeTemp(tmp); + GenImplicitNullCheck(TargetReg(kArg1, kRef), info->opt_flags); } } return call_state; @@ -1353,11 +1368,14 @@ 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); - } else { + mir_graph_->ConstantValue(rl_idx.orig_sreg), nullptr, &comparison); + MarkPossibleNullPointerExceptionAfter(0, comparison); + } 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 c68ad6be4b..4e4f11044c 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -838,6 +838,7 @@ 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,10 +1149,12 @@ 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); + int offset, int check_value, LIR* target, LIR** compare); // 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 8df5b6dfdf..ebe3f0a9fc 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -271,21 +271,22 @@ 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 b7441d7649..40dd9cc105 100644 --- a/compiler/dex/quick/x86/call_x86.cc +++ b/compiler/dex/quick/x86/call_x86.cc @@ -222,15 +222,28 @@ 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. */ - const bool skip_overflow_check = mir_graph_->MethodIsLeaf() && - !IsLargeFrame(frame_size_, cu_->target64 ? kX86_64 : kX86); + 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)); + NewLIR0(kPseudoMethodEntry); /* Spill core callee saves */ SpillCoreRegs(); @@ -260,25 +273,27 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { private: const size_t sp_displace_; }; - // 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( + 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( new(arena_)StackOverflowSlowPath(this, branch, frame_size_ - GetInstructionSetPointerSize(cu_->instruction_set))); + } } FlushIns(ArgLocs, rl_method); @@ -318,4 +333,14 @@ 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 f4fa1b4b17..1f974296fc 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -85,6 +85,7 @@ 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; @@ -805,9 +806,11 @@ 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); + int offset, int check_value, LIR* target, LIR** compare); /* * 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 2f27482e55..3f1df189de 100755 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -1098,6 +1098,7 @@ 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)); @@ -1140,6 +1141,7 @@ 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 bb1f379fe5..61a047413b 100755 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -888,8 +888,12 @@ RegStorage X86Mir2Lir::LoadHelper(ThreadOffset<8> offset) { } LIR* X86Mir2Lir::CheckSuspendUsingLoad() { - LOG(FATAL) << "Unexpected use of CheckSuspendUsingLoad in x86"; - return nullptr; + // 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); } uint64_t X86Mir2Lir::GetTargetInstFlags(int opcode) { @@ -1254,6 +1258,7 @@ 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? @@ -1280,12 +1285,20 @@ 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; @@ -2682,7 +2695,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().GetExplicitNullChecks()) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitNullChecks()) { *pcrLabel = GenExplicitNullCheck(TargetReg(kArg1, kRef), 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 045e58e5a7..047a65d585 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()) { - load2 = NewLIR3(opcode, r_dest.GetHighReg(), r_base.GetReg(), + load = NewLIR3(opcode, r_dest.GetHighReg(), r_base.GetReg(), displacement + HIWORD_OFFSET); - load = NewLIR3(opcode, r_dest.GetLowReg(), r_base.GetReg(), displacement + LOWORD_OFFSET); + load2 = 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(); - load2 = NewLIR5(opcode, temp.GetReg(), r_base.GetReg(), r_index.GetReg(), scale, + load = NewLIR5(opcode, temp.GetReg(), r_base.GetReg(), r_index.GetReg(), scale, displacement + HIWORD_OFFSET); - load = NewLIR5(opcode, r_dest.GetLowReg(), r_base.GetReg(), r_index.GetReg(), scale, + load2 = NewLIR5(opcode, r_dest.GetLowReg(), r_base.GetReg(), r_index.GetReg(), scale, displacement + LOWORD_OFFSET); OpRegCopy(r_dest.GetHigh(), temp); FreeTemp(temp); } else { - load2 = NewLIR5(opcode, r_dest.GetHighReg(), r_base.GetReg(), r_index.GetReg(), scale, + load = NewLIR5(opcode, r_dest.GetHighReg(), r_base.GetReg(), r_index.GetReg(), scale, displacement + HIWORD_OFFSET); - load = NewLIR5(opcode, r_dest.GetLowReg(), r_base.GetReg(), r_index.GetReg(), scale, + load2 = NewLIR5(opcode, r_dest.GetLowReg(), r_base.GetReg(), r_index.GetReg(), scale, displacement + LOWORD_OFFSET); } } else { @@ -744,6 +744,7 @@ 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; } @@ -878,9 +879,12 @@ 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) { - NewLIR3(IS_SIMM8(check_value) ? kX86Cmp32MI8 : kX86Cmp32MI, base_reg.GetReg(), offset, - check_value); + 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; + } 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 56573810ca..17f9b916d4 100644 --- a/compiler/dex/quick/x86/x86_lir.h +++ b/compiler/dex/quick/x86/x86_lir.h @@ -499,6 +499,7 @@ 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 92b2feeb7f..c0f91d1646 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), - explicit_null_checks_(true), - explicit_so_checks_(true), - explicit_suspend_checks_(true) + implicit_null_checks_(false), + implicit_so_checks_(false), + implicit_suspend_checks_(false) #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 explicit_null_checks, - bool explicit_so_checks, - bool explicit_suspend_checks + bool implicit_null_checks, + bool implicit_so_checks, + bool implicit_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), - explicit_null_checks_(explicit_null_checks), - explicit_so_checks_(explicit_so_checks), - explicit_suspend_checks_(explicit_suspend_checks) + implicit_null_checks_(implicit_null_checks), + implicit_so_checks_(implicit_so_checks), + implicit_suspend_checks_(implicit_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 GetExplicitNullChecks() const { - return explicit_null_checks_; + bool GetImplicitNullChecks() const { + return implicit_null_checks_; } - void SetExplicitNullChecks(bool new_val) { - explicit_null_checks_ = new_val; + void SetImplicitNullChecks(bool new_val) { + implicit_null_checks_ = new_val; } - bool GetExplicitStackOverflowChecks() const { - return explicit_so_checks_; + bool GetImplicitStackOverflowChecks() const { + return implicit_so_checks_; } - void SetExplicitStackOverflowChecks(bool new_val) { - explicit_so_checks_ = new_val; + void SetImplicitStackOverflowChecks(bool new_val) { + implicit_so_checks_ = new_val; } - bool GetExplicitSuspendChecks() const { - return explicit_suspend_checks_; + bool GetImplicitSuspendChecks() const { + return implicit_suspend_checks_; } - void SetExplicitSuspendChecks(bool new_val) { - explicit_suspend_checks_ = new_val; + void SetImplicitSuspendChecks(bool new_val) { + implicit_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 explicit_null_checks_; - bool explicit_so_checks_; - bool explicit_suspend_checks_; + bool implicit_null_checks_; + bool implicit_so_checks_; + bool implicit_suspend_checks_; #ifdef ART_SEA_IR_MODE bool sea_ir_mode_; #endif diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 982e6d4f2c..fe4fcd4177 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -25,7 +25,6 @@ #include "elf_fixup.h" #include "gc/space/image_space.h" #include "image_writer.h" -#include "implicit_check_options.h" #include "lock_word.h" #include "mirror/object-inl.h" #include "oat_writer.h" @@ -81,8 +80,6 @@ TEST_F(ImageTest, WriteRead) { t.NewTiming("WriteElf"); ScopedObjectAccess soa(Thread::Current()); SafeMap<std::string, std::string> key_value_store; - key_value_store.Put(ImplicitCheckOptions::kImplicitChecksOatHeaderKey, - ImplicitCheckOptions::Serialize(true, true, true)); OatWriter oat_writer(class_linker->GetBootClassPath(), 0, 0, compiler_driver_.get(), &timings, &key_value_store); bool success = compiler_driver_->WriteElf(GetTestAndroidRoot(), @@ -144,9 +141,6 @@ TEST_F(ImageTest, WriteRead) { std::string image("-Ximage:"); image.append(image_location.GetFilename()); options.push_back(std::make_pair(image.c_str(), reinterpret_cast<void*>(NULL))); - // Turn off implicit checks for this runtime, as we compiled the image with them off. - std::string explicit_checks("-implicit-checks:none"); - options.push_back(std::make_pair(explicit_checks.c_str(), reinterpret_cast<void*>(NULL))); if (!Runtime::Create(options, false)) { LOG(FATAL) << "Failed to create runtime"; diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 1444ca0309..458d5b6e0f 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -20,7 +20,6 @@ #include "dex/quick/dex_file_to_method_inliner_map.h" #include "dex/quick_compiler_callbacks.h" #include "entrypoints/quick/quick_entrypoints.h" -#include "implicit_check_options.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/object_array-inl.h" @@ -118,8 +117,6 @@ TEST_F(OatTest, WriteRead) { ScratchFile tmp; SafeMap<std::string, std::string> key_value_store; key_value_store.Put(OatHeader::kImageLocationKey, "lue.art"); - key_value_store.Put(ImplicitCheckOptions::kImplicitChecksOatHeaderKey, - ImplicitCheckOptions::Serialize(true, true, true)); OatWriter oat_writer(class_linker->GetBootClassPath(), 42U, 4096U, diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 77946b03ae..6d861d4bfe 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -49,7 +49,6 @@ #include "gc/space/image_space.h" #include "gc/space/space-inl.h" #include "image_writer.h" -#include "implicit_check_options.h" #include "leb128.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" @@ -742,20 +741,6 @@ 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; @@ -839,10 +824,10 @@ static int dex2oat(int argc, char** argv) { bool watch_dog_enabled = !kIsTargetBuild; bool generate_gdb_information = kIsDebugBuild; - bool explicit_null_checks = true; - bool explicit_so_checks = true; - bool explicit_suspend_checks = true; - bool has_explicit_checks_options = false; + // Checks are all explicit until we know the architecture. + bool implicit_null_checks = false; + bool implicit_so_checks = false; + bool implicit_suspend_checks = false; for (int i = 0; i < argc; i++) { const StringPiece option(argv[i]); @@ -1019,31 +1004,6 @@ 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; @@ -1176,14 +1136,25 @@ static int dex2oat(int argc, char** argv) { Usage("Unknown --compiler-filter value %s", compiler_filter_string); } - ImplicitCheckOptions::CheckISASupport(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; + } + std::unique_ptr<CompilerOptions> compiler_options(new CompilerOptions(compiler_filter, huge_method_threshold, large_method_threshold, @@ -1194,9 +1165,9 @@ static int dex2oat(int argc, char** argv) { include_patch_information, top_k_profile_threshold, include_debug_symbols, - explicit_null_checks, - explicit_so_checks, - explicit_suspend_checks + implicit_null_checks, + implicit_so_checks, + implicit_suspend_checks #ifdef ART_SEA_IR_MODE , compiler_options.sea_ir_ = true; @@ -1247,7 +1218,7 @@ static int dex2oat(int argc, char** argv) { } std::unique_ptr<VerificationResults> verification_results(new VerificationResults( - compiler_options.get())); + compiler_options.get())); DexFileToMethodInlinerMap method_inliner_map; QuickCompilerCallbacks callbacks(verification_results.get(), &method_inliner_map); runtime_options.push_back(std::make_pair("compilercallbacks", &callbacks)); @@ -1270,18 +1241,6 @@ 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) { - if (ImplicitCheckOptions::CheckForCompiling(kRuntimeISA, instruction_set, &explicit_null_checks, - &explicit_so_checks, &explicit_suspend_checks)) { - compiler_options->SetExplicitNullChecks(explicit_null_checks); - compiler_options->SetExplicitStackOverflowChecks(explicit_so_checks); - compiler_options->SetExplicitSuspendChecks(explicit_suspend_checks); - } - } - - // 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. Thread* self = Thread::Current(); @@ -1384,14 +1343,6 @@ static int dex2oat(int argc, char** argv) { std::unique_ptr<SafeMap<std::string, std::string> > key_value_store( new SafeMap<std::string, std::string>()); - // Insert implicit check options. - key_value_store->Put(ImplicitCheckOptions::kImplicitChecksOatHeaderKey, - ImplicitCheckOptions::Serialize(compiler_options->GetExplicitNullChecks(), - compiler_options-> - GetExplicitStackOverflowChecks(), - compiler_options-> - GetExplicitSuspendChecks())); - // Insert some compiler things. std::ostringstream oss; for (int i = 0; i < argc; ++i) { diff --git a/runtime/Android.mk b/runtime/Android.mk index f2d3c8e8c0..d2fc2298e2 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -416,6 +416,7 @@ $$(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 2a82129511..e22c56ec69 100644 --- a/runtime/arch/arm/fault_handler_arm.cc +++ b/runtime/arch/arm/fault_handler_arm.cc @@ -46,9 +46,10 @@ static uint32_t GetInstructionSize(uint8_t* pc) { return instr_size; } -void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method, +void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context, + mirror::ArtMethod** out_method, uintptr_t* out_return_pc, uintptr_t* out_sp) { - struct ucontext *uc = (struct ucontext *)context; + struct ucontext* uc = reinterpret_cast<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; @@ -114,7 +115,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 = (struct ucontext *)context; + struct ucontext* uc = reinterpret_cast<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; @@ -178,7 +179,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 = (struct ucontext *)context; + struct ucontext* uc = reinterpret_cast<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; @@ -205,7 +206,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 - // the exists below the protected region. Determine the address of the next + // that 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 74c3023aff..34eede605c 100644 --- a/runtime/arch/arm64/fault_handler_arm64.cc +++ b/runtime/arch/arm64/fault_handler_arm64.cc @@ -29,7 +29,8 @@ namespace art { -void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method, +void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, 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 1ecd7d964b..5a64a698f1 100644 --- a/runtime/arch/mips/fault_handler_mips.cc +++ b/runtime/arch/mips/fault_handler_mips.cc @@ -29,7 +29,8 @@ namespace art { -void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method, +void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, 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 7c1980e57b..435f280a6b 100644 --- a/runtime/arch/x86/fault_handler_x86.cc +++ b/runtime/arch/x86/fault_handler_x86.cc @@ -21,7 +21,21 @@ #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" +#if defined(__APPLE__) +#define ucontext __darwin_ucontext +#define CTX_ESP uc_mcontext->__ss.__esp +#define CTX_EIP uc_mcontext->__ss.__eip +#define CTX_EAX uc_mcontext->__ss.__eax +#else +#define CTX_ESP uc_mcontext.gregs[REG_ESP] +#define CTX_EIP uc_mcontext.gregs[REG_EIP] +#define CTX_EAX uc_mcontext.gregs[REG_EAX] +#endif // // X86 specific fault handler functions. @@ -29,19 +43,292 @@ namespace art { -void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method, +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, uintptr_t* out_return_pc, uintptr_t* out_sp) { + struct ucontext* uc = reinterpret_cast<struct ucontext*>(context); + *out_sp = static_cast<uintptr_t>(uc->CTX_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->CTX_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->CTX_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) { - return false; + struct ucontext *uc = reinterpret_cast<struct ucontext*>(context); + uint8_t* pc = reinterpret_cast<uint8_t*>(uc->CTX_EIP); + uint8_t* sp = reinterpret_cast<uint8_t*>(uc->CTX_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->CTX_ESP = reinterpret_cast<uint32_t>(next_sp); + + uc->CTX_EIP = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception); + VLOG(signals) << "Generating null pointer exception"; + return true; } +// 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->CTX_EIP); + uint8_t* sp = reinterpret_cast<uint8_t*>(uc->CTX_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->CTX_ESP = reinterpret_cast<uint32_t>(next_sp); + + uc->CTX_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) { - return false; + struct ucontext *uc = reinterpret_cast<struct ucontext*>(context); + uintptr_t sp = static_cast<uintptr_t>(uc->CTX_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->CTX_EAX = pregion; + + // Now arrange for the signal handler to return to art_quick_throw_stack_overflow_from_signal. + uc->CTX_EIP = reinterpret_cast<uintptr_t>(art_quick_throw_stack_overflow_from_signal); + + return true; } } // namespace art diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 24b9e465e8..68f46ad26c 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -173,6 +173,21 @@ 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 233d3c7d1a..88ae7f3711 100644 --- a/runtime/arch/x86_64/fault_handler_x86_64.cc +++ b/runtime/arch/x86_64/fault_handler_x86_64.cc @@ -29,7 +29,8 @@ namespace art { -void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method, +void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, 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 b4355cad5c..1b916284c5 100644 --- a/runtime/fault_handler.cc +++ b/runtime/fault_handler.cc @@ -18,12 +18,9 @@ #include <sys/mman.h> #include <sys/ucontext.h> - #include "mirror/art_method.h" #include "mirror/class.h" -#ifdef HAVE_ANDROID_OS #include "sigchain.h" -#endif #include "thread-inl.h" #include "verify_object-inl.h" @@ -40,6 +37,7 @@ 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); } @@ -48,10 +46,6 @@ FaultManager::FaultManager() { } FaultManager::~FaultManager() { -#ifdef HAVE_ANDROID_OS - UnclaimSignalChain(SIGSEGV); -#endif - sigaction(SIGSEGV, &oldaction_, nullptr); // Restore old handler. } @@ -65,11 +59,12 @@ void FaultManager::Init() { #endif // Set our signal handler now. - sigaction(SIGSEGV, &action, &oldaction_); -#ifdef HAVE_ANDROID_OS + int e = sigaction(SIGSEGV, &action, &oldaction_); + if (e != 0) { + VLOG(signals) << "Failed to claim SEGV: " << strerror(errno); + } // 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) { @@ -77,8 +72,12 @@ 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(context, true)) { + if (IsInGeneratedCode(info, context, true)) { VLOG(signals) << "in generated code, looking for handler"; for (const auto& handler : generated_code_handlers_) { VLOG(signals) << "invoking Action on handler " << handler; @@ -94,11 +93,8 @@ void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) { } art_sigsegv_fault(); -#ifdef HAVE_ANDROID_OS + // Pass this on to the next handler in the chain, or the default if none. InvokeUserSignalHandler(sig, info, context); -#else - oldaction_.sa_sigaction(sig, info, context); -#endif } void FaultManager::AddHandler(FaultHandler* handler, bool generated_code) { @@ -125,7 +121,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(void* context, bool check_dex_pc) { +bool FaultManager::IsInGeneratedCode(siginfo_t* siginfo, 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"; @@ -154,7 +150,7 @@ bool FaultManager::IsInGeneratedCode(void* context, bool check_dex_pc) { // Get the architecture specific method address and return address. These // are in architecture specific files in arch/<arch>/fault_handler_<arch>. - GetMethodAndReturnPCAndSP(context, &method_obj, &return_pc, &sp); + GetMethodAndReturnPCAndSP(siginfo, context, &method_obj, &return_pc, &sp); // If we don't have a potential method, we're outta here. VLOG(signals) << "potential method: " << method_obj; @@ -235,12 +231,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(context, false)) { + if (manager_->IsInGeneratedCode(siginfo, 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(context, &method, &return_pc, &sp); + manager_->GetMethodAndReturnPCAndSP(siginfo, 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 026f5b9c4a..71c99771cd 100644 --- a/runtime/fault_handler.h +++ b/runtime/fault_handler.h @@ -43,9 +43,10 @@ class FaultManager { void HandleFault(int sig, siginfo_t* info, void* context); void AddHandler(FaultHandler* handler, bool generated_code); void RemoveHandler(FaultHandler* handler); - void GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** out_method, + void GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context, mirror::ArtMethod** out_method, uintptr_t* out_return_pc, uintptr_t* out_sp); - bool IsInGeneratedCode(void *context, bool check_dex_pc) NO_THREAD_SAFETY_ANALYSIS; + bool IsInGeneratedCode(siginfo_t* siginfo, void *context, bool check_dex_pc) + NO_THREAD_SAFETY_ANALYSIS; private: std::vector<FaultHandler*> generated_code_handlers_; diff --git a/runtime/implicit_check_options.h b/runtime/implicit_check_options.h deleted file mode 100644 index a6595b88e0..0000000000 --- a/runtime/implicit_check_options.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - * 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_RUNTIME_IMPLICIT_CHECK_OPTIONS_H_ -#define ART_RUNTIME_IMPLICIT_CHECK_OPTIONS_H_ - -#include "gc/heap.h" -#include "gc/space/image_space.h" -#include "instruction_set.h" -#include "runtime.h" - -#include <string> - -namespace art { - -class ImplicitCheckOptions { - public: - static constexpr const char* kImplicitChecksOatHeaderKey = "implicit-checks"; - - static std::string Serialize(bool explicit_null_checks, bool explicit_stack_overflow_checks, - bool explicit_suspend_checks) { - char tmp[4]; - tmp[0] = explicit_null_checks ? 'N' : 'n'; - tmp[1] = explicit_stack_overflow_checks ? 'O' : 'o'; - tmp[2] = explicit_suspend_checks ? 'S' : 's'; - tmp[3] = 0; - return std::string(tmp); - } - - static bool Parse(const char* str, bool* explicit_null_checks, - bool* explicit_stack_overflow_checks, bool* explicit_suspend_checks) { - if (str != nullptr && str[0] != 0 && str[1] != 0 && str[2] != 0 && - (str[0] == 'n' || str[0] == 'N') && - (str[1] == 'o' || str[1] == 'O') && - (str[2] == 's' || str[2] == 'S')) { - *explicit_null_checks = str[0] == 'N'; - *explicit_stack_overflow_checks = str[1] == 'O'; - *explicit_suspend_checks = str[2] == 'S'; - return true; - } else { - return false; - } - } - - // Check whether the given flags are correct with respect to the current runtime and the given - // executable flag. - static bool CheckRuntimeSupport(bool executable, bool explicit_null_checks, - bool explicit_stack_overflow_checks, - bool explicit_suspend_checks, std::string* error_msg) { - if (!executable) { - // Not meant to be run, i.e., either we are compiling or dumping. Just accept. - return true; - } - - Runtime* runtime = Runtime::Current(); - // We really should have a runtime. - DCHECK_NE(static_cast<Runtime*>(nullptr), runtime); - - if (runtime->GetInstrumentation()->IsForcedInterpretOnly()) { - // We are an interpret-only environment. Ignore the check value. - return true; - } - - if (runtime->ExplicitNullChecks() != explicit_null_checks || - runtime->ExplicitStackOverflowChecks() != explicit_stack_overflow_checks || - runtime->ExplicitSuspendChecks() != explicit_suspend_checks) { - if (error_msg != nullptr) { - // Create an error message. - - std::ostringstream os; - os << "Explicit check options do not match runtime: "; - os << runtime->ExplicitNullChecks() << " vs " << explicit_null_checks << " | "; - os << runtime->ExplicitStackOverflowChecks() << " vs " << explicit_stack_overflow_checks - << " | "; - os << runtime->ExplicitSuspendChecks() << " vs " << explicit_suspend_checks; - - *error_msg = os.str(); - } - - // Currently we do not create correct images when pre-opting, so the emulator will fail with - // this change. Once the change is in the tree, REMOVE. - if (true) { - // At least try to log it, though. - if (error_msg != nullptr) { - LOG(WARNING) << *error_msg; - } - return true; - } else { - return false; - } - } - - // Accepted. - return true; - } - - // Check (and override) the flags depending on current support in the ISA. - // Right now will reset all flags to explicit except on ARM. - static void CheckISASupport(InstructionSet isa, bool* explicit_null_checks, - bool* explicit_stack_overflow_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_stack_overflow_checks = true; - *explicit_suspend_checks = true; - } - } - - static bool CheckForCompiling(InstructionSet host, InstructionSet target, - bool* explicit_null_checks, bool* explicit_stack_overflow_checks, - bool* explicit_suspend_checks) { - // Check the boot image settings. - Runtime* runtime = Runtime::Current(); - if (runtime != nullptr) { - gc::space::ImageSpace* ispace = runtime->GetHeap()->GetImageSpace(); - if (ispace != nullptr) { - const OatFile* oat_file = ispace->GetOatFile(); - if (oat_file != nullptr) { - const char* v = oat_file->GetOatHeader().GetStoreValueByKey(kImplicitChecksOatHeaderKey); - if (!Parse(v, explicit_null_checks, explicit_stack_overflow_checks, - explicit_suspend_checks)) { - LOG(FATAL) << "Should have been able to parse boot image implicit check values"; - } - return true; - } - } - } - - // Check the current runtime. - bool cross_compiling = true; - switch (host) { - case kArm: - case kThumb2: - cross_compiling = target != kArm && target != kThumb2; - break; - default: - cross_compiling = host != target; - break; - } - if (!cross_compiling) { - Runtime* runtime = Runtime::Current(); - *explicit_null_checks = runtime->ExplicitNullChecks(); - *explicit_stack_overflow_checks = runtime->ExplicitStackOverflowChecks(); - *explicit_suspend_checks = runtime->ExplicitSuspendChecks(); - return true; - } - - // Give up. - return false; - } -}; - -} // namespace art - -#endif // ART_RUNTIME_IMPLICIT_CHECK_OPTIONS_H_ diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 9cefcb6b1a..86c1baef27 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -23,7 +23,6 @@ #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "elf_file.h" -#include "implicit_check_options.h" #include "oat.h" #include "mirror/art_method.h" #include "mirror/art_method-inl.h" @@ -80,36 +79,7 @@ OatFile* OatFile::Open(const std::string& filename, } ret.reset(OpenElfFile(file.get(), location, requested_base, false, executable, error_msg)); } - - if (ret.get() == nullptr) { - return nullptr; - } - - // Embedded options check. Right now only implicit checks. - // TODO: Refactor to somewhere else? - const char* implicit_checks_value = ret->GetOatHeader(). - GetStoreValueByKey(ImplicitCheckOptions::kImplicitChecksOatHeaderKey); - - if (implicit_checks_value == nullptr) { - *error_msg = "Did not find implicit checks value."; - return nullptr; - } - - bool explicit_null_checks, explicit_so_checks, explicit_suspend_checks; - if (ImplicitCheckOptions::Parse(implicit_checks_value, &explicit_null_checks, - &explicit_so_checks, &explicit_suspend_checks)) { - // Check whether the runtime agrees with the recorded checks. - if (ImplicitCheckOptions::CheckRuntimeSupport(executable, explicit_null_checks, - explicit_so_checks, explicit_suspend_checks, - error_msg)) { - return ret.release(); - } else { - return nullptr; - } - } else { - *error_msg = "Failed parsing implicit check options."; - return nullptr; - } + return ret.release(); } OatFile* OatFile::OpenWritable(File* file, const std::string& location, std::string* error_msg) { diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 577691c90b..9a1d0f71aa 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -265,38 +265,6 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize 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; @@ -312,6 +280,7 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize 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++; @@ -589,54 +558,6 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize 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 b1de62a54f..23f2bcfa62 100644 --- a/runtime/parsed_options.h +++ b/runtime/parsed_options.h @@ -93,10 +93,6 @@ 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_; // Whether or not we use homogeneous space compaction to avoid OOM errors. If enabled, // the heap will attempt to create an extra space which enables compacting from a malloc space to // another malloc space when we are about to throw OOM. diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 0ddd2aed4a..aca2607bfc 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -141,7 +141,10 @@ Runtime::Runtime() suspend_handler_(nullptr), stack_overflow_handler_(nullptr), verify_(false), - target_sdk_version_(0) { + target_sdk_version_(0), + implicit_null_checks_(false), + implicit_so_checks_(false), + implicit_suspend_checks_(false) { for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { callee_save_methods_[i] = nullptr; } @@ -581,41 +584,6 @@ bool Runtime::Init(const RuntimeOptions& 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_, @@ -648,6 +616,42 @@ bool Runtime::Init(const RuntimeOptions& 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(); @@ -1222,37 +1226,6 @@ 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 fccccbdfd7..284e4ffe30 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -589,6 +589,11 @@ 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 9fa158d5e0..6d3ba5d9bf 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -232,47 +232,95 @@ 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. + + const bool running_on_intel = (kRuntimeISA == kX86) || (kRuntimeISA == kX86_64); + + if (running_on_intel) { + // On Intel, 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. + // 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_intel) { + mprotect(pregion - kStackOverflowProtectedSize, kStackOverflowProtectedSize, PROT_NONE); + madvise(stack_lowmem, stack_top - stack_lowmem, MADV_DONTNEED); + } return; } // Add marker so that we can detect a second attempt to do this. *marker = kMarker; - 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; + if (!running_on_intel) { + // Running on !Intel, 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; + 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) { - madvise(pregion, kStackOverflowProtectedSize, MADV_DONTNEED); + if (running_on_intel) { + // 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); + } } } @@ -533,13 +581,17 @@ void Thread::InitStackHwm() { // Install the protected region if we are doing implicit overflow checks. if (implicit_stack_check) { if (is_main_thread) { - // The main thread has a 16K protected region at the bottom. We need + 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 // to install our own region so we need to move the limits // of the stack to make room for it. - constexpr uint32_t kDelta = 16 * KB; - tlsPtr_.stack_begin += kDelta; - tlsPtr_.stack_end += kDelta; - tlsPtr_.stack_size -= kDelta; + tlsPtr_.stack_begin += guardsize; + tlsPtr_.stack_end += guardsize; + tlsPtr_.stack_size -= guardsize; } InstallImplicitProtection(is_main_thread); } diff --git a/sigchainlib/Android.mk b/sigchainlib/Android.mk index 8e25339d9e..d86735d120 100644 --- a/sigchainlib/Android.mk +++ b/sigchainlib/Android.mk @@ -23,8 +23,23 @@ LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) LOCAL_MODULE_TAGS := optional LOCAL_CFLAGS += $(ART_TARGET_CFLAGS) LOCAL_SRC_FILES := sigchain.cc +LOCAL_CLANG = $(ART_TARGET_CLANG) LOCAL_MODULE:= libsigchain 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_CLANG = $(ART_HOST_CLANG) +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 5a5805fe4f..6f93083832 100644 --- a/sigchainlib/sigchain.cc +++ b/sigchainlib/sigchain.cc @@ -14,12 +14,22 @@ * 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> #include <stdlib.h> +#if defined(__APPLE__) +#define _NSIG NSIG +#endif + namespace art { class SignalAction { @@ -67,7 +77,11 @@ 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); } @@ -104,10 +118,16 @@ 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 16300bba54..2b142db890 100644 --- a/test/Android.oat.mk +++ b/test/Android.oat.mk @@ -203,6 +203,7 @@ $(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 \ |