X64: Let debugger patch JSReturn with a debug break.

Review URL: http://codereview.chromium.org/155286

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2421 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
whesse@chromium.org 2009-07-09 13:30:27 +00:00
parent e0ac466589
commit b019600dce
4 changed files with 84 additions and 45 deletions

View File

@ -363,6 +363,10 @@ class Debug {
static const int kIa32CallInstructionLength = 5;
static const int kIa32JSReturnSequenceLength = 6;
// The x64 JS return sequence is padded with int3 to make it large
// enough to hold a call instruction when the debugger patches it.
static const int kX64JSReturnSequenceLength = 13;
// Code generator routines.
static void GenerateLoadICDebugBreak(MacroAssembler* masm);
static void GenerateStoreICDebugBreak(MacroAssembler* masm);

View File

@ -73,45 +73,8 @@ XMMRegister xmm14 = { 14 };
XMMRegister xmm15 = { 15 };
Operand::Operand(Register base, int32_t disp): rex_(0) {
len_ = 1;
if (base.is(rsp) || base.is(r12)) {
// SIB byte is needed to encode (rsp + offset) or (r12 + offset).
set_sib(times_1, rsp, base);
}
if (disp == 0 && !base.is(rbp) && !base.is(r13)) {
set_modrm(0, base);
} else if (is_int8(disp)) {
set_modrm(1, base);
set_disp8(disp);
} else {
set_modrm(2, base);
set_disp32(disp);
}
}
Operand::Operand(Register base,
Register index,
ScaleFactor scale,
int32_t disp): rex_(0) {
ASSERT(!index.is(rsp));
len_ = 1;
set_sib(scale, index, base);
if (disp == 0 && !base.is(rbp) && !base.is(r13)) {
// This call to set_modrm doesn't overwrite the REX.B (or REX.X) bits
// possibly set by set_sib.
set_modrm(0, rsp);
} else if (is_int8(disp)) {
set_modrm(1, rsp);
set_disp8(disp);
} else {
set_modrm(2, rsp);
set_disp32(disp);
}
}
// -----------------------------------------------------------------------------
// Implementation of CpuFeatures
// The required user mode extensions in X64 are (from AMD64 ABI Table A.1):
// fpu, tsc, cx8, cmov, mmx, sse, sse2, fxsr, syscall
@ -193,6 +156,71 @@ void CpuFeatures::Probe() {
ASSERT(IsSupported(CMOV));
}
// -----------------------------------------------------------------------------
// Implementation of RelocInfo
// Patch the code at the current PC with a call to the target address.
// Additional guard int3 instructions can be added if required.
void RelocInfo::PatchCodeWithCall(Address target, int guard_bytes) {
// Call instruction takes up 13 bytes and int3 takes up one byte.
Address patch_site = pc_;
Memory::uint16_at(patch_site) = 0xBA49u; // movq r10, imm64
// Write "0x00, call r10" starting at last byte of address. We overwrite
// the 0x00 later, and this lets us write a uint32.
Memory::uint32_at(patch_site + 9) = 0xD2FF4900u; // 0x00, call r10
Memory::Address_at(patch_site + 2) = target;
// Add the requested number of int3 instructions after the call.
for (int i = 0; i < guard_bytes; i++) {
*(patch_site + 13 + i) = 0xCC; // int3
}
}
// -----------------------------------------------------------------------------
// Implementation of Operand
Operand::Operand(Register base, int32_t disp): rex_(0) {
len_ = 1;
if (base.is(rsp) || base.is(r12)) {
// SIB byte is needed to encode (rsp + offset) or (r12 + offset).
set_sib(times_1, rsp, base);
}
if (disp == 0 && !base.is(rbp) && !base.is(r13)) {
set_modrm(0, base);
} else if (is_int8(disp)) {
set_modrm(1, base);
set_disp8(disp);
} else {
set_modrm(2, base);
set_disp32(disp);
}
}
Operand::Operand(Register base,
Register index,
ScaleFactor scale,
int32_t disp): rex_(0) {
ASSERT(!index.is(rsp));
len_ = 1;
set_sib(scale, index, base);
if (disp == 0 && !base.is(rbp) && !base.is(r13)) {
// This call to set_modrm doesn't overwrite the REX.B (or REX.X) bits
// possibly set by set_sib.
set_modrm(0, rsp);
} else if (is_int8(disp)) {
set_modrm(1, rsp);
set_disp8(disp);
} else {
set_modrm(2, rsp);
set_disp32(disp);
}
}
// -----------------------------------------------------------------------------
// Implementation of Assembler

View File

@ -355,14 +355,19 @@ void CodeGenerator::GenerateReturnSequence(Result* return_value) {
// receiver.
frame_->Exit();
masm_->ret((scope_->num_parameters() + 1) * kPointerSize);
// Add padding that will be overwritten by a debugger breakpoint.
// frame_->Exit() generates "movq rsp, rbp; pop rbp" length 5.
// "ret k" has length 2.
const int kPadding = Debug::kX64JSReturnSequenceLength - 5 - 2;
for (int i = 0; i < kPadding; ++i) {
masm_->int3();
}
DeleteFrame();
// TODO(x64): introduce kX64JSReturnSequenceLength and enable assert.
// Check that the size of the code used for returning matches what is
// expected by the debugger.
// ASSERT_EQ(Debug::kIa32JSReturnSequenceLength,
// masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
ASSERT_EQ(Debug::kX64JSReturnSequenceLength,
masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
}

View File

@ -38,8 +38,10 @@ namespace internal {
#ifdef ENABLE_DEBUGGER_SUPPORT
bool Debug::IsDebugBreakAtReturn(v8::internal::RelocInfo* rinfo) {
UNIMPLEMENTED();
return false;
ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()));
// 11th byte of patch is 0x49, 11th byte of JS return is 0xCC (int3).
ASSERT(*(rinfo->pc() + 10) == 0x49 || *(rinfo->pc() + 10) == 0xCC);
return (*(rinfo->pc() + 10) == 0x49);
}
void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) {