summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Allison <dallison@google.com>2014-07-16 16:04:32 -0700
committerDave Allison <dallison@google.com>2014-08-07 09:26:35 -0700
commitdfd3b47813c14c5f1607cbe7b10a28b1b2f29cbc (patch)
treeb71dbae2ef905c08eb4cf615c8b44868cc314531
parent8b62dc0f993d0445401655fc274e5225498fa81c (diff)
downloadart-dfd3b47813c14c5f1607cbe7b10a28b1b2f29cbc.zip
art-dfd3b47813c14c5f1607cbe7b10a28b1b2f29cbc.tar.gz
art-dfd3b47813c14c5f1607cbe7b10a28b1b2f29cbc.tar.bz2
Add implicit checks for x86_64 architecture.
This combines the x86 and x86_64 fault handlers into one. It also merges in the change to the entrypoints for X86_64. Replaces generic instruction length calculator with one that only works with the specific instructions we use. Bug: 16256184 Change-Id: I1e8ab5ad43f46060de9597615b423c89a836035c Signed-off-by: Chao-ying Fu <chao-ying.fu@intel.com>
-rwxr-xr-xcompiler/dex/quick/x86/target_x86.cc24
-rw-r--r--dex2oat/dex2oat.cc1
-rw-r--r--runtime/Android.mk4
-rw-r--r--runtime/arch/arm/fault_handler_arm.cc2
-rw-r--r--runtime/arch/arm64/fault_handler_arm64.cc2
-rw-r--r--runtime/arch/mips/fault_handler_mips.cc2
-rw-r--r--runtime/arch/x86/fault_handler_x86.cc266
-rw-r--r--runtime/arch/x86_64/fault_handler_x86_64.cc48
-rw-r--r--runtime/arch/x86_64/quick_entrypoints_x86_64.S12
-rw-r--r--runtime/fault_handler.cc6
-rw-r--r--runtime/fault_handler.h10
-rw-r--r--runtime/runtime.cc1
-rw-r--r--runtime/thread_linux.cc13
13 files changed, 217 insertions, 174 deletions
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index 69f3e67..b78b90c 100755
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -880,9 +880,13 @@ RegStorage X86Mir2Lir::LoadHelper(QuickEntrypointEnum trampoline) {
LIR* X86Mir2Lir::CheckSuspendUsingLoad() {
// First load the pointer in fs:[suspend-trigger] into eax
// Then use a test instruction to indirect via that address.
- NewLIR2(kX86Mov32RT, rs_rAX.GetReg(), cu_->target64 ?
- Thread::ThreadSuspendTriggerOffset<8>().Int32Value() :
- Thread::ThreadSuspendTriggerOffset<4>().Int32Value());
+ if (cu_->target64) {
+ NewLIR2(kX86Mov64RT, rs_rAX.GetReg(),
+ Thread::ThreadSuspendTriggerOffset<8>().Int32Value());
+ } else {
+ NewLIR2(kX86Mov32RT, rs_rAX.GetReg(),
+ Thread::ThreadSuspendTriggerOffset<4>().Int32Value());
+ }
return NewLIR3(kX86Test32RM, rs_rAX.GetReg(), rs_rAX.GetReg(), 0);
}
@@ -1293,14 +1297,14 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
// Compute the number of words to search in to rCX.
Load32Disp(rs_rDX, count_offset, rs_rCX);
- if (!cu_->target64) {
- // 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);
+ // 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);
+ if (!cu_->target64) {
// EDI is callee-save register in 32-bit mode.
NewLIR1(kX86Push32R, rs_rDI.GetReg());
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index ac3eb39..bb86a74 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1169,6 +1169,7 @@ static int dex2oat(int argc, char** argv) {
case kThumb2:
case kArm64:
case kX86:
+ case kX86_64:
implicit_null_checks = true;
implicit_so_checks = true;
break;
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 302e835..09ec004 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -248,6 +248,8 @@ LIBART_SRC_FILES_x86 := \
LIBART_TARGET_SRC_FILES_x86 := \
$(LIBART_SRC_FILES_x86)
+# Note that the fault_handler_x86.cc is not a mistake. This file is
+# shared between the x86 and x86_64 architectures.
LIBART_SRC_FILES_x86_64 := \
arch/x86_64/context_x86_64.cc \
arch/x86_64/entrypoints_init_x86_64.cc \
@@ -257,7 +259,7 @@ LIBART_SRC_FILES_x86_64 := \
arch/x86_64/quick_entrypoints_x86_64.S \
arch/x86_64/thread_x86_64.cc \
monitor_pool.cc \
- arch/x86_64/fault_handler_x86_64.cc
+ arch/x86/fault_handler_x86.cc
LIBART_TARGET_SRC_FILES_x86_64 := \
$(LIBART_SRC_FILES_x86_64) \
diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc
index 48582f4..be28544 100644
--- a/runtime/arch/arm/fault_handler_arm.cc
+++ b/runtime/arch/arm/fault_handler_arm.cc
@@ -46,7 +46,7 @@ static uint32_t GetInstructionSize(uint8_t* pc) {
return instr_size;
}
-void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context,
+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);
diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc
index dc82cc2..3a7e689 100644
--- a/runtime/arch/arm64/fault_handler_arm64.cc
+++ b/runtime/arch/arm64/fault_handler_arm64.cc
@@ -37,7 +37,7 @@ extern "C" void art_quick_implicit_suspend();
namespace art {
-void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context,
+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);
diff --git a/runtime/arch/mips/fault_handler_mips.cc b/runtime/arch/mips/fault_handler_mips.cc
index 5a64a69..0e76aab 100644
--- a/runtime/arch/mips/fault_handler_mips.cc
+++ b/runtime/arch/mips/fault_handler_mips.cc
@@ -29,7 +29,7 @@
namespace art {
-void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context,
+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 435f280..8b6c9b1 100644
--- a/runtime/arch/x86/fault_handler_x86.cc
+++ b/runtime/arch/x86/fault_handler_x86.cc
@@ -31,14 +31,21 @@
#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
+#elif defined(__x86_64__)
+#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
#define CTX_ESP uc_mcontext.gregs[REG_ESP]
#define CTX_EIP uc_mcontext.gregs[REG_EIP]
#define CTX_EAX uc_mcontext.gregs[REG_EAX]
+#define CTX_METHOD uc_mcontext.gregs[REG_EAX]
#endif
//
-// X86 specific fault handler functions.
+// X86 (and X86_64) specific fault handler functions.
//
namespace art {
@@ -47,129 +54,146 @@ 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;
+// Return 0 if the instruction is not handled.
+static uint32_t GetInstructionSize(const uint8_t* pc) {
+#if defined(__x86_64)
+ const bool x86_64 = true;
+#else
+ const bool x86_64 = false;
+#endif
- // 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:
+ const uint8_t* startpc = pc;
+
+ uint8_t opcode = *pc++;
+ uint8_t modrm;
+ bool has_modrm = false;
+ bool two_byte = false;
+ uint32_t displacement_size = 0;
+ uint32_t immediate_size = 0;
+
+ // Prefixes.
+ while (true) {
+ bool prefix_present = false;
+ switch (opcode) {
+ // Group 1
+ case 0xf0:
+ case 0xf2:
+ case 0xf3:
+
+ // Group 2
+ case 0x2e:
+ case 0x36:
+ case 0x3e:
+ case 0x26:
+ case 0x64:
+ case 0x65:
+
+ // Group 3
case 0x66:
- // Group 4 - address size override:
+
+ // Group 4
case 0x67:
- break;
- default:
- have_prefixes = false;
+ opcode = *pc++;
+ prefix_present = true;
break;
}
- if (have_prefixes) {
- pc++;
+ if (!prefix_present) {
+ break;
}
- } 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;
+ if (x86_64 && opcode >= 0x40 && opcode <= 0x4f) {
+ opcode = *pc++;
}
- // Unknown instruction, work out length.
-
- // Work out if we have a ModR/M byte.
- uint8_t opcode = *pc++;
- if (opcode == 0xf) {
+ if (opcode == 0x0f) {
+ // Two byte opcode
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.
+ bool unhandled_instruction = false;
- // 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;
+ switch (opcode) {
+ case 0x10: // vmovsd/ss
+ case 0x11: // vmovsd/ss
+ case 0xb6: // movzx
+ case 0xb7:
+ case 0xbe: // movsx
+ case 0xbf:
+ modrm = *pc++;
+ has_modrm = true;
+ break;
+ default:
+ unhandled_instruction = true;
+ break;
+ }
+ } else {
+ switch (opcode) {
+ case 0x89: // mov
+ case 0x8b:
+ case 0x38: // cmp with memory.
+ case 0x39:
+ case 0x3a:
+ case 0x3b:
+ case 0x3c:
+ case 0x3d:
+ case 0x85: // test.
+ modrm = *pc++;
+ has_modrm = true;
+ break;
+
+ case 0x80: // group 1, byte immediate.
+ case 0x83:
+ modrm = *pc++;
+ has_modrm = true;
+ immediate_size = 1;
+ break;
+
+ case 0x81: // group 1, word immediate.
+ modrm = *pc++;
+ has_modrm = true;
+ immediate_size = 4;
+ break;
+
+ default:
+ unhandled_instruction = true;
+ break;
+ }
+ }
+
+ if (unhandled_instruction) {
+ VLOG(signals) << "Unhandled x86 instruction with opcode " << static_cast<int>(opcode);
+ return 0;
}
if (has_modrm) {
- uint8_t modrm = *pc++;
uint8_t mod = (modrm >> 6) & 0b11;
- uint8_t reg = (modrm >> 3) & 0b111;
+
+ // Check for SIB.
+ if (mod != 0b11 && (modrm & 0b111) == 4) {
+ ++pc; // SIB
+ }
+
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:
+ case 0b00: break;
+ case 0b01: displacement_size = 1; break;
+ case 0b10: displacement_size = 4; break;
+ case 0b11:
break;
}
}
- VLOG(signals) << "calculated X86 instruction size is " << (pc - instruction_start);
- return pc - instruction_start;
+ // Skip displacement and immediate.
+ pc += displacement_size + immediate_size;
+
+ VLOG(signals) << "x86 instruction length calculated as " << (pc - startpc);
+ return pc - startpc;
}
-void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context,
+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);
@@ -180,21 +204,30 @@ void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context,
}
// 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.
+ // get the method from the top of the stack. However it's in EAX(x86)/RDI(x86_64).
uintptr_t* fault_addr = reinterpret_cast<uintptr_t*>(siginfo->si_addr);
uintptr_t* overflow_addr = reinterpret_cast<uintptr_t*>(
+#if defined(__x86_64__)
+ reinterpret_cast<uint8_t*>(*out_sp) - GetStackOverflowReservedBytes(kX86_64));
+#else
reinterpret_cast<uint8_t*>(*out_sp) - GetStackOverflowReservedBytes(kX86));
+#endif
if (overflow_addr == fault_addr) {
- *out_method = reinterpret_cast<mirror::ArtMethod*>(uc->CTX_EAX);
+ *out_method = reinterpret_cast<mirror::ArtMethod*>(uc->CTX_METHOD);
} else {
// The method is at the top of the stack.
- *out_method = reinterpret_cast<mirror::ArtMethod*>(reinterpret_cast<uintptr_t*>(*out_sp)[0]);
+ *out_method = (reinterpret_cast<StackReference<mirror::ArtMethod>* >(*out_sp)[0]).AsMirrorPtr();
}
uint8_t* pc = reinterpret_cast<uint8_t*>(uc->CTX_EIP);
VLOG(signals) << HexDump(pc, 32, true, "PC ");
uint32_t instr_size = GetInstructionSize(pc);
+ if (instr_size == 0) {
+ // Unknown instruction, tell caller it's not ours.
+ *out_method = nullptr;
+ return;
+ }
*out_return_pc = reinterpret_cast<uintptr_t>(pc + instr_size);
}
@@ -204,16 +237,21 @@ bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) {
uint8_t* sp = reinterpret_cast<uint8_t*>(uc->CTX_ESP);
uint32_t instr_size = GetInstructionSize(pc);
+ if (instr_size == 0) {
+ // Unknown instruction, can't really happen.
+ return false;
+ }
+
// 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);
+ uintptr_t retaddr = reinterpret_cast<uintptr_t>(pc + instr_size);
+ uintptr_t* next_sp = reinterpret_cast<uintptr_t*>(sp - sizeof(uintptr_t));
*next_sp = retaddr;
- uc->CTX_ESP = reinterpret_cast<uint32_t>(next_sp);
+ uc->CTX_ESP = reinterpret_cast<uintptr_t>(next_sp);
uc->CTX_EIP = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception);
VLOG(signals) << "Generating null pointer exception";
@@ -221,9 +259,14 @@ bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) {
}
// A suspend check is done using the following instruction sequence:
+// (x86)
// 0xf720f1df: 648B058C000000 mov eax, fs:[0x8c] ; suspend_trigger
// .. some intervening instructions.
// 0xf720f1e6: 8500 test eax, [eax]
+// (x86_64)
+// 0x7f579de45d9e: 65488B0425A8000000 movq rax, gs:[0xa8] ; suspend_trigger
+// .. some intervening instructions.
+// 0x7f579de45da7: 8500 test eax, [eax]
// The offset from fs is Thread::ThreadSuspendTriggerOffset().
// To check for a suspend check, we examine the instructions that caused
@@ -231,11 +274,20 @@ bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) {
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.
+#if defined(__x86_64__)
+ uint32_t trigger = Thread::ThreadSuspendTriggerOffset<8>().Int32Value();
+#else
uint32_t trigger = Thread::ThreadSuspendTriggerOffset<4>().Int32Value();
+#endif
VLOG(signals) << "Checking for suspension point";
+#if defined(__x86_64__)
+ uint8_t checkinst1[] = {0x65, 0x48, 0x8b, 0x04, 0x25, static_cast<uint8_t>(trigger & 0xff),
+ static_cast<uint8_t>((trigger >> 8) & 0xff), 0, 0};
+#else
uint8_t checkinst1[] = {0x64, 0x8b, 0x05, static_cast<uint8_t>(trigger & 0xff),
static_cast<uint8_t>((trigger >> 8) & 0xff), 0, 0};
+#endif
uint8_t checkinst2[] = {0x85, 0x00};
struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
@@ -270,10 +322,10 @@ bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) {
// 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);
+ uintptr_t retaddr = reinterpret_cast<uintptr_t>(pc + 2);
+ uintptr_t* next_sp = reinterpret_cast<uintptr_t*>(sp - sizeof(uintptr_t));
*next_sp = retaddr;
- uc->CTX_ESP = reinterpret_cast<uint32_t>(next_sp);
+ uc->CTX_ESP = reinterpret_cast<uintptr_t>(next_sp);
uc->CTX_EIP = reinterpret_cast<uintptr_t>(art_quick_test_suspend);
@@ -302,7 +354,11 @@ bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) {
VLOG(signals) << "checking for stack overflow, sp: " << std::hex << sp <<
", fault_addr: " << fault_addr;
+#if defined(__x86_64__)
+ uintptr_t overflow_addr = sp - GetStackOverflowReservedBytes(kX86_64);
+#else
uintptr_t overflow_addr = sp - GetStackOverflowReservedBytes(kX86);
+#endif
Thread* self = Thread::Current();
uintptr_t pregion = reinterpret_cast<uintptr_t>(self->GetStackEnd()) -
diff --git a/runtime/arch/x86_64/fault_handler_x86_64.cc b/runtime/arch/x86_64/fault_handler_x86_64.cc
deleted file mode 100644
index 88ae7f3..0000000
--- a/runtime/arch/x86_64/fault_handler_x86_64.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#include "fault_handler.h"
-#include <sys/ucontext.h>
-#include "base/macros.h"
-#include "globals.h"
-#include "base/logging.h"
-#include "base/hex_dump.h"
-
-
-//
-// X86_64 specific fault handler functions.
-//
-
-namespace art {
-
-void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context,
- mirror::ArtMethod** out_method,
- uintptr_t* out_return_pc, uintptr_t* out_sp) {
-}
-
-bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) {
- return false;
-}
-
-bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) {
- return false;
-}
-
-bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) {
- return false;
-}
-} // namespace art
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 48bc240..f021ada 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -284,6 +284,18 @@ 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, 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.
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 1b91628..8ddaf5c 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -76,6 +76,7 @@ void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) {
// Also, there is only an 8K stack available here to logging can cause memory
// overwrite issues if you are unlucky. If you want to enable logging and
// are getting crashes, allocate more space for the alternate signal stack.
+
VLOG(signals) << "Handling fault";
if (IsInGeneratedCode(info, context, true)) {
VLOG(signals) << "in generated code, looking for handler";
@@ -91,6 +92,7 @@ void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) {
return;
}
}
+
art_sigsegv_fault();
// Pass this on to the next handler in the chain, or the default if none.
@@ -150,7 +152,7 @@ bool FaultManager::IsInGeneratedCode(siginfo_t* siginfo, void* context, bool che
// Get the architecture specific method address and return address. These
// are in architecture specific files in arch/<arch>/fault_handler_<arch>.
- GetMethodAndReturnPCAndSP(siginfo, context, &method_obj, &return_pc, &sp);
+ GetMethodAndReturnPcAndSp(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;
@@ -236,7 +238,7 @@ bool JavaStackTraceHandler::Action(int sig, siginfo_t* siginfo, void* context) {
mirror::ArtMethod* method = nullptr;
uintptr_t return_pc = 0;
uintptr_t sp = 0;
- manager_->GetMethodAndReturnPCAndSP(siginfo, 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 71c9977..1acd024 100644
--- a/runtime/fault_handler.h
+++ b/runtime/fault_handler.h
@@ -43,8 +43,14 @@ class FaultManager {
void HandleFault(int sig, siginfo_t* info, void* context);
void AddHandler(FaultHandler* handler, bool generated_code);
void RemoveHandler(FaultHandler* handler);
- void GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context, mirror::ArtMethod** out_method,
- uintptr_t* out_return_pc, uintptr_t* out_sp);
+
+ // Note that the following two functions are called in the context of a signal handler.
+ // The IsInGeneratedCode() function checks that the mutator lock is held before it
+ // calls GetMethodAndReturnPCAndSP().
+ // TODO: think about adding lock assertions and fake lock and unlock functions.
+ void GetMethodAndReturnPcAndSp(siginfo_t* siginfo, void* context, mirror::ArtMethod** out_method,
+ uintptr_t* out_return_pc, uintptr_t* out_sp)
+ NO_THREAD_SAFETY_ANALYSIS;
bool IsInGeneratedCode(siginfo_t* siginfo, void *context, bool check_dex_pc)
NO_THREAD_SAFETY_ANALYSIS;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index fe877d5..e0c0d63 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -621,6 +621,7 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized)
case kThumb2:
case kX86:
case kArm64:
+ case kX86_64:
implicit_null_checks_ = true;
implicit_so_checks_ = true;
break;
diff --git a/runtime/thread_linux.cc b/runtime/thread_linux.cc
index ee66ccc..9aacb30 100644
--- a/runtime/thread_linux.cc
+++ b/runtime/thread_linux.cc
@@ -32,11 +32,18 @@ static void SigAltStack(stack_t* new_stack, stack_t* old_stack) {
}
}
+// The default SIGSTKSZ on linux is 8K. If we do any logging in a signal
+// handler this is too small. We allocate 16K instead.
+static constexpr int kHostAltSigStackSize = 16*1024; // 16K signal stack.
+
void Thread::SetUpAlternateSignalStack() {
// Create and set an alternate signal stack.
+#ifdef HAVE_ANDROID_OS
+ LOG(FATAL) << "Invalid use of alternate signal stack on Android";
+#endif
stack_t ss;
- ss.ss_sp = new uint8_t[SIGSTKSZ];
- ss.ss_size = SIGSTKSZ;
+ ss.ss_sp = new uint8_t[kHostAltSigStackSize];
+ ss.ss_size = kHostAltSigStackSize;
ss.ss_flags = 0;
CHECK(ss.ss_sp != NULL);
SigAltStack(&ss, NULL);
@@ -56,7 +63,7 @@ void Thread::TearDownAlternateSignalStack() {
// Tell the kernel to stop using it.
ss.ss_sp = NULL;
ss.ss_flags = SS_DISABLE;
- ss.ss_size = SIGSTKSZ; // Avoid ENOMEM failure with Mac OS' buggy libc.
+ ss.ss_size = kHostAltSigStackSize; // Avoid ENOMEM failure with Mac OS' buggy libc.
SigAltStack(&ss, NULL);
// Free it.