* We can safely skip the stack overflow check if we're
* a leaf *and* our frame size < fudge factor.
*/
- bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !IsLargeFrame(frame_size_, kArm);
+ bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, kArm);
NewLIR0(kPseudoMethodEntry);
- const size_t kStackOverflowReservedUsableBytes = GetStackOverflowReservedBytes(kArm) -
- Thread::kStackOverflowSignalReservedBytes;
+ const size_t kStackOverflowReservedUsableBytes = GetStackOverflowReservedBytes(kArm);
bool large_frame = (static_cast<size_t>(frame_size_) > kStackOverflowReservedUsableBytes);
+ bool generate_explicit_stack_overflow_check = large_frame ||
+ !cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks();
if (!skip_overflow_check) {
- if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) {
+ if (generate_explicit_stack_overflow_check) {
if (!large_frame) {
/* Load stack limit */
LockTemp(rs_r12);
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 (generate_explicit_stack_overflow_check) {
class StackOverflowSlowPath : public LIRSlowPath {
public:
StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, bool restore_lr, size_t sp_displace)
* We can safely skip the stack overflow check if we're
* a leaf *and* our frame size < fudge factor.
*/
- bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !IsLargeFrame(frame_size_, kArm64);
+ bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, kArm64);
NewLIR0(kPseudoMethodEntry);
+ const size_t kStackOverflowReservedUsableBytes = GetStackOverflowReservedBytes(kArm64);
+ const bool large_frame = static_cast<size_t>(frame_size_) > kStackOverflowReservedUsableBytes;
+ bool generate_explicit_stack_overflow_check = large_frame ||
+ !cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks();
const int spill_count = num_core_spills_ + num_fp_spills_;
const int spill_size = (spill_count * kArm64PointerSize + 15) & ~0xf; // SP 16 byte alignment.
const int frame_size_without_spills = frame_size_ - spill_size;
if (!skip_overflow_check) {
- if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) {
+ if (generate_explicit_stack_overflow_check) {
// Load stack limit
LoadWordDisp(rs_xSELF, Thread::StackEndOffset<8>().Int32Value(), rs_xIP1);
} else {
}
if (!skip_overflow_check) {
- if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) {
+ if (generate_explicit_stack_overflow_check) {
class StackOverflowSlowPath: public LIRSlowPath {
public:
StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, size_t sp_displace) :
* We can safely skip the stack overflow check if we're
* a leaf *and* our frame size < fudge factor.
*/
- bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !IsLargeFrame(frame_size_, kMips);
+ bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, kMips);
NewLIR0(kPseudoMethodEntry);
RegStorage check_reg = AllocTemp();
RegStorage new_sp = AllocTemp();
* 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);
+ bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(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.
codegen_(codegen) {}
void CodeGeneratorARM::GenerateFrameEntry() {
- bool skip_overflow_check = IsLeafMethod() && !IsLargeFrame(GetFrameSize(), InstructionSet::kArm);
+ bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
if (!skip_overflow_check) {
if (kExplicitStackOverflowCheck) {
SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM();
static const int kFakeReturnRegister = 8;
core_spill_mask_ |= (1 << kFakeReturnRegister);
- bool skip_overflow_check = IsLeafMethod() && !IsLargeFrame(GetFrameSize(), InstructionSet::kX86);
+ bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86);
if (!skip_overflow_check && !kExplicitStackOverflowCheck) {
__ testl(EAX, Address(ESP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86))));
RecordPcInfo(0);
Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize));
bool skip_overflow_check = IsLeafMethod()
- && !IsLargeFrame(GetFrameSize(), InstructionSet::kX86_64);
+ && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86_64);
if (!skip_overflow_check) {
if (kExplicitStackOverflowCheck) {
// Determine whether a frame is small or large, used in the decision on whether to elide a
// stack overflow check on method entry.
//
-// A frame is considered large when it's either above kLargeFrameSize, or a quarter of the
-// overflow-usable stack space.
-static inline bool IsLargeFrame(size_t size, InstructionSet isa) {
- return size >= kLargeFrameSize || size >= GetStackOverflowReservedBytes(isa) / 4;
+// A frame is considered large when it's above kLargeFrameSize.
+static inline bool FrameNeedsStackCheck(size_t size, InstructionSet isa) {
+ return size >= kLargeFrameSize;
}
} // namespace art
// Offset of field Thread::tls32_.thin_lock_thread_id verified in InitCpu
#define THREAD_ID_OFFSET 12
// Offset of field Thread::tlsPtr_.card_table verified in InitCpu
-#define THREAD_CARD_TABLE_OFFSET 112
+#define THREAD_CARD_TABLE_OFFSET 120
// Offset of field Thread::tlsPtr_.exception verified in InitCpu
-#define THREAD_EXCEPTION_OFFSET 116
+#define THREAD_EXCEPTION_OFFSET 124
#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 176
#define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 32
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_throw_stack_overflow();
extern "C" void art_quick_implicit_suspend();
// Get the size of a thumb2 instruction in bytes.
uintptr_t overflow_addr = sp - GetStackOverflowReservedBytes(kArm);
- Thread* self = reinterpret_cast<Thread*>(sc->arm_r9);
- CHECK_EQ(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.
- uintptr_t prevsp = sp;
- sp = pregion;
- VLOG(signals) << "setting sp to overflow region at " << std::hex << sp;
-
- // Since the compiler puts the implicit overflow
- // check before the callee save instructions, the SP is already pointing to
- // the previous frame.
- VLOG(signals) << "previous frame: " << std::hex << prevsp;
-
- // Now establish the stack pointer for the signal return.
- sc->arm_sp = prevsp;
-
- // Tell the stack overflow code where the new stack pointer should be.
- sc->arm_ip = sp; // aka r12
+ VLOG(signals) << "Stack overflow found";
- // Now arrange for the signal handler to return to art_quick_throw_stack_overflow_from_signal.
+ // Now arrange for the signal handler to return to art_quick_throw_stack_overflow_from.
// The value of LR must be the same as it was when we entered the code that
// caused this fault. This will be inserted into a callee save frame by
- // the function to which this handler returns (art_quick_throw_stack_overflow_from_signal).
- sc->arm_pc = reinterpret_cast<uintptr_t>(art_quick_throw_stack_overflow_from_signal);
+ // the function to which this handler returns (art_quick_throw_stack_overflow).
+ sc->arm_pc = reinterpret_cast<uintptr_t>(art_quick_throw_stack_overflow);
// The kernel will now return to the address in sc->arm_pc.
return true;
*/
ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFromCode
- /*
- * Invoke stack overflow exception from signal handler.
- * On entry:
- * r9: thread
- * sp: address of last known frame
- * r12: address of next valid SP below protected region in stack
- *
- * This is deceptively simple but hides some complexity. It is called in the case of
- * a stack overflow condition during implicit checks. The signal handler has been
- * called by the kernel due to a load from the protected stack region. The handler
- * works out the address of the previous frame and passes this in SP. However there
- * is a piece of memory somewhere below the current SP that is not accessible (the
- * memory that caused the signal). The signal handler works out the next
- * accessible value of SP and passes this in r12. This code then sets up the SP
- * to be this new value and calls the code to create and throw the stack overflow
- * exception.
- */
-ENTRY art_quick_throw_stack_overflow_from_signal
- SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context
- mov r0, r9 @ pass Thread::Current
- mov r1, sp @ pass SP
- mov sp, r12 @ move SP down to below protected region.
- b artThrowStackOverflowFromCode @ artThrowStackOverflowFromCode(Thread*, SP)
-END art_quick_throw_stack_overflow_from_signal
-
/*
* All generated callsites for interface invokes and invocation slow paths will load arguments
* as usual - except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
// Offset of field Thread::suspend_count_
#define THREAD_FLAGS_OFFSET 0
// Offset of field Thread::card_table_
-#define THREAD_CARD_TABLE_OFFSET 112
+#define THREAD_CARD_TABLE_OFFSET 120
// Offset of field Thread::exception_
-#define THREAD_EXCEPTION_OFFSET 120
+#define THREAD_EXCEPTION_OFFSET 128
// Offset of field Thread::thin_lock_thread_id_
#define THREAD_ID_OFFSET 12
#include "thread.h"
#include "thread-inl.h"
-extern "C" void art_quick_throw_stack_overflow_from_signal();
+extern "C" void art_quick_throw_stack_overflow();
extern "C" void art_quick_throw_null_pointer_exception();
extern "C" void art_quick_implicit_suspend();
uintptr_t overflow_addr = sp - GetStackOverflowReservedBytes(kArm64);
- Thread* self = reinterpret_cast<Thread*>(sc->regs[art::arm64::TR]);
- CHECK_EQ(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.
- uintptr_t prevsp = sp;
- sp = pregion;
- VLOG(signals) << "setting sp to overflow region at " << std::hex << sp;
-
- // Since the compiler puts the implicit overflow
- // check before the callee save instructions, the SP is already pointing to
- // the previous frame.
- VLOG(signals) << "previous frame: " << std::hex << prevsp;
-
- // Now establish the stack pointer for the signal return.
- sc->sp = prevsp;
-
- // Tell the stack overflow code where the new stack pointer should be.
- sc->regs[art::arm64::IP0] = sp; // aka x16
+ VLOG(signals) << "Stack overflow found";
- // Now arrange for the signal handler to return to art_quick_throw_stack_overflow_from_signal.
+ // Now arrange for the signal handler to return to art_quick_throw_stack_overflow.
// The value of LR must be the same as it was when we entered the code that
// caused this fault. This will be inserted into a callee save frame by
- // the function to which this handler returns (art_quick_throw_stack_overflow_from_signal).
- sc->pc = reinterpret_cast<uintptr_t>(art_quick_throw_stack_overflow_from_signal);
+ // the function to which this handler returns (art_quick_throw_stack_overflow).
+ sc->pc = reinterpret_cast<uintptr_t>(art_quick_throw_stack_overflow);
// The kernel will now return to the address in sc->pc.
return true;
*/
ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFromCode
- /*
- * Invoke stack overflow exception from signal handler.
- * On entry:
- * xSELF: thread
- * SP: address of last known frame
- * IP0: address of next valid SP below protected region in stack
- *
- * This is deceptively simple but hides some complexity. It is called in the case of
- * a stack overflow condition during implicit checks. The signal handler has been
- * called by the kernel due to a load from the protected stack region. The handler
- * works out the address of the previous frame and passes this in SP. However there
- * is a piece of memory somewhere below the current SP that is not accessible (the
- * memory that caused the signal). The signal handler works out the next
- * accessible value of SP and passes this in x16/IP0. This code then sets up the SP
- * to be this new value and calls the code to create and throw the stack overflow
- * exception.
- */
-ENTRY art_quick_throw_stack_overflow_from_signal
- SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context
- mov x0, xSELF // pass Thread::Current
- mov x1, sp // pass SP
- mov sp, xIP0 // move SP down to below protected region.
- b artThrowStackOverflowFromCode // artThrowStackOverflowFromCode(Thread*, SP)
-END art_quick_throw_stack_overflow_from_signal
-
/*
* All generated callsites for interface invokes and invocation slow paths will load arguments
* as usual - except instead of loading arg0/x0 with the target Method*, arg0/x0 will contain
#include "asm_support.h"
// Offset of field Thread::self_ verified in InitCpu
-#define THREAD_SELF_OFFSET 148
+#define THREAD_SELF_OFFSET 156
// Offset of field Thread::card_table_ verified in InitCpu
-#define THREAD_CARD_TABLE_OFFSET 112
+#define THREAD_CARD_TABLE_OFFSET 120
// Offset of field Thread::exception_ verified in InitCpu
-#define THREAD_EXCEPTION_OFFSET 116
+#define THREAD_EXCEPTION_OFFSET 124
// Offset of field Thread::thin_lock_thread_id_ verified in InitCpu
#define THREAD_ID_OFFSET 12
#if defined(__APPLE__)
#define ucontext __darwin_ucontext
+
+#if defined(__x86_64__)
+// 64 bit mac build.
+#define CTX_ESP uc_mcontext->__ss.__rsp
+#define CTX_EIP uc_mcontext->__ss.__rip
+#define CTX_EAX uc_mcontext->__ss.__rax
+#define CTX_METHOD uc_mcontext->__ss.__rdi
+#else
+// 32 bit mac build.
#define CTX_ESP uc_mcontext->__ss.__esp
#define CTX_EIP uc_mcontext->__ss.__eip
#define CTX_EAX uc_mcontext->__ss.__eax
#define CTX_METHOD uc_mcontext->__ss.__eax
+#endif
+
#elif defined(__x86_64__)
+// 64 bit linux build.
#define CTX_ESP uc_mcontext.gregs[REG_RSP]
#define CTX_EIP uc_mcontext.gregs[REG_RIP]
#define CTX_EAX uc_mcontext.gregs[REG_RAX]
#define CTX_METHOD uc_mcontext.gregs[REG_RDI]
#else
+// 32 bit linux build.
#define CTX_ESP uc_mcontext.gregs[REG_ESP]
#define CTX_EIP uc_mcontext.gregs[REG_EIP]
#define CTX_EAX uc_mcontext.gregs[REG_EAX]
namespace art {
+#if defined(__APPLE__) && defined(__x86_64__)
+// mac symbols have a prefix of _ on x86_64
+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();
+#define EXT_SYM(sym) _ ## sym
+#else
extern "C" void art_quick_throw_null_pointer_exception();
-extern "C" void art_quick_throw_stack_overflow_from_signal();
+extern "C" void art_quick_throw_stack_overflow();
extern "C" void art_quick_test_suspend();
+#define EXT_SYM(sym) sym
+#endif
// Get the size of an instruction in bytes.
// Return 0 if the instruction is not handled.
*next_sp = retaddr;
uc->CTX_ESP = reinterpret_cast<uintptr_t>(next_sp);
- uc->CTX_EIP = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception);
+ uc->CTX_EIP = reinterpret_cast<uintptr_t>(EXT_SYM(art_quick_throw_null_pointer_exception));
VLOG(signals) << "Generating null pointer exception";
return true;
}
*next_sp = retaddr;
uc->CTX_ESP = reinterpret_cast<uintptr_t>(next_sp);
- uc->CTX_EIP = reinterpret_cast<uintptr_t>(art_quick_test_suspend);
+ uc->CTX_EIP = reinterpret_cast<uintptr_t>(EXT_SYM(art_quick_test_suspend));
// Now remove the suspend trigger that caused this fault.
Thread::Current()->RemoveSuspendTrigger();
uintptr_t overflow_addr = sp - GetStackOverflowReservedBytes(kX86);
#endif
- 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;
+ VLOG(signals) << "Stack overflow found";
// 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);
+ // Now arrange for the signal handler to return to art_quick_throw_stack_overflow.
+ uc->CTX_EIP = reinterpret_cast<uintptr_t>(art_quick_throw_stack_overflow);
return true;
}
*/
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.
#define RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET 16
// Offset of field Thread::self_ verified in InitCpu
-#define THREAD_SELF_OFFSET 184
+#define THREAD_SELF_OFFSET 192
// Offset of field Thread::card_table_ verified in InitCpu
-#define THREAD_CARD_TABLE_OFFSET 112
+#define THREAD_CARD_TABLE_OFFSET 120
// Offset of field Thread::exception_ verified in InitCpu
-#define THREAD_EXCEPTION_OFFSET 120
+#define THREAD_EXCEPTION_OFFSET 128
// Offset of field Thread::thin_lock_thread_id_ verified in InitCpu
#define THREAD_ID_OFFSET 12
*/
NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFromCode
-// On entry to this function, RAX contains the ESP value for the overflow region.
-DEFINE_FUNCTION art_quick_throw_stack_overflow_from_signal
- // Here, the RSP is above the protected region. We need to create a
- // callee save frame and then move RSP down to the overflow region.
- SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context
- mov %rsp, %rsi // get current stack pointer, pass SP as second arg
- mov %rax, %rsp // move RSP to the overflow region.
- mov %gs:THREAD_SELF_OFFSET, %rdi // pass Thread::Current() as first arg
- 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.
bool explicit_overflow_check = Runtime::Current()->ExplicitStackOverflowChecks();
self->ResetDefaultStackEnd(!explicit_overflow_check); // Return to default stack size.
+
+ // And restore protection if implicit checks are on.
+ if (!explicit_overflow_check) {
+ self->ProtectStack();
+ }
}
void CheckReferenceResult(mirror::Object* o, Thread* self) {
StackReference<mirror::ArtMethod>* sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+ self->NoteSignalBeingHandled();
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
ThrowNullPointerExceptionFromDexPC(throw_location);
+ self->NoteSignalHandlerDone();
self->QuickDeliverException();
}
extern "C" void artThrowStackOverflowFromCode(Thread* self, StackReference<mirror::ArtMethod>* sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+ self->NoteSignalBeingHandled();
ThrowStackOverflowError(self);
+ self->NoteSignalHandlerDone();
self->QuickDeliverException();
}
EXPECT_OFFSET_DIFFP(Thread, tls32_, daemon, throwing_OutOfMemoryError, 4);
EXPECT_OFFSET_DIFFP(Thread, tls32_, throwing_OutOfMemoryError, no_thread_suspension, 4);
EXPECT_OFFSET_DIFFP(Thread, tls32_, no_thread_suspension, thread_exit_check_count, 4);
+ EXPECT_OFFSET_DIFFP(Thread, tls32_, thread_exit_check_count,
+ is_exception_reported_to_instrumentation_, 4);
+ EXPECT_OFFSET_DIFFP(Thread, tls32_, is_exception_reported_to_instrumentation_,
+ handling_signal_, 4);
// TODO: Better connection. Take alignment into account.
EXPECT_OFFSET_DIFF_GT3(Thread, tls32_.thread_exit_check_count, tls64_.trace_clock_base, 4,
static constexpr size_t kDefaultStackOverflowReservedBytes = 16 * KB;
static constexpr size_t kMipsStackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
-// TODO: Lower once implicit stack-overflow checks can work with less than 16K.
-static constexpr size_t kArmStackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB;
-static constexpr size_t kArm64StackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB;
-static constexpr size_t kX86StackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB;
-static constexpr size_t kX86_64StackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB;
+static constexpr size_t kArmStackOverflowReservedBytes = 8 * KB;
+static constexpr size_t kArm64StackOverflowReservedBytes = 8 * KB;
+static constexpr size_t kX86StackOverflowReservedBytes = 8 * KB;
+static constexpr size_t kX86_64StackOverflowReservedBytes = 8 * KB;
size_t GetStackOverflowReservedBytes(InstructionSet isa) {
switch (isa) {
void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
const char* shorty) {
+ if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) {
+ ThrowStackOverflowError(self);
+ return;
+ }
+
if (kIsDebugBuild) {
self->AssertThreadSuspensionIsAllowable();
CHECK_EQ(kRunnable, self->GetState());
namespace art {
const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '3', '8', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '3', '9', '\0' };
static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
size_t estimate = 0U;
JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid,
va_list args)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // We want to make sure that the stack is not within a small distance from the
+ // protected region in case we are calling into a leaf function whose stack
+ // check has been elided.
+ if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) {
+ ThrowStackOverflowError(soa.Self());
+ return JValue();
+ }
+
mirror::ArtMethod* method = soa.DecodeMethod(mid);
mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj);
uint32_t shorty_len = 0;
JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, mirror::Object* receiver,
jmethodID mid, jvalue* args) {
+ // We want to make sure that the stack is not within a small distance from the
+ // protected region in case we are calling into a leaf function whose stack
+ // check has been elided.
+ if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) {
+ ThrowStackOverflowError(soa.Self());
+ return JValue();
+ }
+
mirror::ArtMethod* method = soa.DecodeMethod(mid);
uint32_t shorty_len = 0;
const char* shorty = method->GetShorty(&shorty_len);
JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnable& soa,
mirror::Object* receiver, jmethodID mid, jvalue* args) {
+ // We want to make sure that the stack is not within a small distance from the
+ // protected region in case we are calling into a leaf function whose stack
+ // check has been elided.
+ if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) {
+ ThrowStackOverflowError(soa.Self());
+ return JValue();
+ }
+
mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
uint32_t shorty_len = 0;
const char* shorty = method->GetShorty(&shorty_len);
JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
jobject obj, jmethodID mid, va_list args) {
+ // We want to make sure that the stack is not within a small distance from the
+ // protected region in case we are calling into a leaf function whose stack
+ // check has been elided.
+ if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) {
+ ThrowStackOverflowError(soa.Self());
+ return JValue();
+ }
+
mirror::Object* receiver = soa.Decode<mirror::Object*>(obj);
mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid));
uint32_t shorty_len = 0;
void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg_offset,
MethodHelper& mh, JValue* result) {
+ // We want to make sure that the stack is not within a small distance from the
+ // protected region in case we are calling into a leaf function whose stack
+ // check has been elided.
+ if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) {
+ ThrowStackOverflowError(self);
+ return;
+ }
+
ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
arg_array.BuildArgArrayFromFrame(shadow_frame, arg_offset);
shadow_frame->GetMethod()->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result,
jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaMethod,
jobject javaReceiver, jobject javaArgs, bool accessible) {
+ // We want to make sure that the stack is not within a small distance from the
+ // protected region in case we are calling into a leaf function whose stack
+ // check has been elided.
+ if (UNLIKELY(__builtin_frame_address(0) <
+ soa.Self()->GetStackEndForInterpreter(true))) {
+ ThrowStackOverflowError(soa.Self());
+ return nullptr;
+ }
+
mirror::ArtMethod* m = mirror::ArtMethod::FromReflectedMethod(soa, javaMethod);
mirror::Class* declaring_class = m->GetDeclaringClass();
bool Thread::is_started_ = false;
pthread_key_t Thread::pthread_key_self_;
ConditionVariable* Thread::resume_cond_ = nullptr;
-const size_t Thread::kStackOverflowImplicitCheckSize = kStackOverflowProtectedSize +
- GetStackOverflowReservedBytes(kRuntimeISA);
+const size_t Thread::kStackOverflowImplicitCheckSize = GetStackOverflowReservedBytes(kRuntimeISA);
static const char* kThreadNameDuringStartup = "<native thread without managed peer>";
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.
+// overflow is detected. It is located right below the stack_begin_.
//
-// 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
+// There is a little complexity here that deserves a special mention. On some
+// architectures, the stack created using a VM_GROWSDOWN 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) &
+void Thread::InstallImplicitProtection() {
+ byte* pregion = tlsPtr_.stack_begin - kStackOverflowProtectedSize;
+ byte* stack_himem = tlsPtr_.stack_end;
+ byte* stack_top = reinterpret_cast<byte*>(reinterpret_cast<uintptr_t>(&stack_himem) &
~(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;
- }
- }
- }
+ // First remove the protection on the protected region as will want to read and
+ // write it. This may fail (on the first attempt when the stack is not mapped)
+ // but we ignore that.
+ UnprotectStack();
- // 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_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;
+ // Map in the stack. This must be done by reading from the
+ // current stack pointer downwards as the stack may be mapped using VM_GROWSDOWN
+ // in the kernel. Any access more than a page below the current SP might cause
+ // a segv.
- 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;
- }
+ // Read every page from the high address to the low.
+ for (byte* p = stack_top; p > pregion; p -= kPageSize) {
+ dont_optimize_this = *p;
}
- 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) << kStackOverflowProtectedSize;
- }
+ // Protect the bottom of the stack to prevent read/write to it.
+ ProtectStack();
// 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_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);
- }
- }
+ uint32_t unwanted_size = stack_top - pregion - kPageSize;
+ madvise(pregion, unwanted_size, MADV_DONTNEED);
}
void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) {
tlsPtr_.stack_begin = reinterpret_cast<byte*>(read_stack_base);
tlsPtr_.stack_size = read_stack_size;
- if (read_stack_size <= GetStackOverflowReservedBytes(kRuntimeISA)) {
+ // The minimum stack size we can cope with is the overflow reserved bytes (typically
+ // 8K) + the protected region size (4K) + another page (4K). Typically this will
+ // be 8+4+4 = 16K. The thread won't be able to do much with this stack even the GC takes
+ // between 8K and 12K.
+ uint32_t min_stack = GetStackOverflowReservedBytes(kRuntimeISA) + kStackOverflowProtectedSize
+ + 4 * KB;
+ if (read_stack_size <= min_stack) {
LOG(FATAL) << "Attempt to attach a thread with a too-small stack (" << read_stack_size
<< " bytes)";
}
// 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
- // 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;
- }
- InstallImplicitProtection(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 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.
+ tlsPtr_.stack_begin += guardsize;
+ tlsPtr_.stack_end += guardsize;
+ tlsPtr_.stack_size -= guardsize;
+
+ InstallImplicitProtection();
}
// Sanity check.
}
tlsPtr_.stack_end = tlsPtr_.stack_begin;
+
+ // Remove the stack overflow protection if is it set up.
+ bool implicit_stack_check = !Runtime::Current()->ExplicitStackOverflowChecks();
+ if (implicit_stack_check) {
+ if (!UnprotectStack()) {
+ LOG(ERROR) << "Unable to remove stack protection for stack overflow";
+ }
+ }
}
void Thread::SetTlab(byte* start, byte* end) {
return os;
}
+void Thread::ProtectStack() {
+ void* pregion = tlsPtr_.stack_begin - kStackOverflowProtectedSize;
+ VLOG(threads) << "Protecting stack at " << pregion;
+ if (mprotect(pregion, kStackOverflowProtectedSize, PROT_NONE) == -1) {
+ LOG(FATAL) << "Unable to create protected region in stack for implicit overflow check. "
+ "Reason: "
+ << strerror(errno) << " size: " << kStackOverflowProtectedSize;
+ }
+}
+
+bool Thread::UnprotectStack() {
+ void* pregion = tlsPtr_.stack_begin - kStackOverflowProtectedSize;
+ VLOG(threads) << "Unprotecting stack at " << pregion;
+ return mprotect(pregion, kStackOverflowProtectedSize, PROT_READ|PROT_WRITE) == 0;
+}
+
+
} // namespace art
static constexpr size_t kNumRosAllocThreadLocalSizeBrackets = 34;
+// Thread's stack layout for implicit stack overflow checks:
+//
+// +---------------------+ <- highest address of stack memory
+// | |
+// . . <- SP
+// | |
+// | |
+// +---------------------+ <- stack_end
+// | |
+// | Gap |
+// | |
+// +---------------------+ <- stack_begin
+// | |
+// | Protected region |
+// | |
+// +---------------------+ <- lowest address of stack memory
+//
+// The stack always grows down in memory. At the lowest address is a region of memory
+// that is set mprotect(PROT_NONE). Any attempt to read/write to this region will
+// result in a segmentation fault signal. At any point, the thread's SP will be somewhere
+// between the stack_end and the highest address in stack memory. An implicit stack
+// overflow check is a read of memory at a certain offset below the current SP (4K typically).
+// If the thread's SP is below the stack_end address this will be a read into the protected
+// region. If the SP is above the stack_end address, the thread is guaranteed to have
+// at least 4K of space. Because stack overflow checks are only performed in generated code,
+// if the thread makes a call out to a native function (through JNI), that native function
+// might only have 4K of memory (if the SP is adjacent to stack_end).
+
class Thread {
public:
- // How much of the reserved bytes is reserved for incoming signals.
- static constexpr size_t kStackOverflowSignalReservedBytes = 2 * KB;
-
// For implicit overflow checks we reserve an extra piece of memory at the bottom
// of the stack (lowest memory). The higher portion of the memory
// is protected against reads and the lower is available for use while
// throwing the StackOverflow exception.
- static constexpr size_t kStackOverflowProtectedSize = 16 * KB;
+ static constexpr size_t kStackOverflowProtectedSize = 4 * KB;
static const size_t kStackOverflowImplicitCheckSize;
// Creates a new native thread corresponding to the given managed peer.
}
// Install the protected region for implicit stack checks.
- void InstallImplicitProtection(bool is_main_stack);
+ void InstallImplicitProtection();
bool IsHandlingStackOverflow() const {
return tlsPtr_.stack_end == tlsPtr_.stack_begin;
tls32_.is_exception_reported_to_instrumentation_ = reported;
}
+ void ProtectStack();
+ bool UnprotectStack();
+
+ void NoteSignalBeingHandled() {
+ if (tls32_.handling_signal_) {
+ LOG(FATAL) << "Detected signal while processing a signal";
+ }
+ tls32_.handling_signal_ = true;
+ }
+
+ void NoteSignalHandlerDone() {
+ tls32_.handling_signal_ = false;
+ }
+
private:
explicit Thread(bool daemon);
~Thread() LOCKS_EXCLUDED(Locks::mutator_lock_,
explicit tls_32bit_sized_values(bool is_daemon) :
suspend_count(0), debug_suspend_count(0), thin_lock_thread_id(0), tid(0),
daemon(is_daemon), throwing_OutOfMemoryError(false), no_thread_suspension(0),
- thread_exit_check_count(0), is_exception_reported_to_instrumentation_(false) {
+ thread_exit_check_count(0), is_exception_reported_to_instrumentation_(false),
+ handling_signal_(false), padding_(0) {
}
union StateAndFlags state_and_flags;
// When true this field indicates that the exception associated with this thread has already
// been reported to instrumentation.
bool32_t is_exception_reported_to_instrumentation_;
+
+ // True if signal is being handled by this thread.
+ bool32_t handling_signal_;
+
+ // Padding to make the size aligned to 8. Remove this if we add another 32 bit field.
+ int32_t padding_;
} tls32_;
struct PACKED(8) tls_64bit_sized_values {
Integer intobj = new Integer(0);
String s = "asdf";
long start = System.currentTimeMillis();
- for (int i = 0; i < 1000000; i++) {
+ for (int i = 0; i < 10000; i++) {
map.put(intobj, s);
}
long end = System.currentTimeMillis();
Integer intobj = new Integer(0);
String s = "asdf";
long start = System.currentTimeMillis();
- for (int i = 0; i < 1000000; i++) {
+ for (int i = 0; i < 10000; i++) {
map.put(intobj, s);
}
long end = System.currentTimeMillis();
#include <signal.h>
#include <stdio.h>
+#include <stdlib.h>
#include <unistd.h>
#include "jni.h"
#include <sys/ucontext.h>
#endif
+static int signal_count;
+static const int kMaxSignal = 2;
+
static void signalhandler(int sig, siginfo_t* info, void* context) {
printf("signal caught\n");
+ ++signal_count;
+ if (signal_count > kMaxSignal) {
+ abort();
+ }
#ifdef __arm__
// On ARM we do a more exhaustive test to make sure the signal
// context is OK.