Implement code patcher for x64.

Use the code patcher for the patching of the JS return sequence used by the debugger.

Added explicit instruction cache flushing in a code patching section which did not have it for completeness, even though it is not required on Intel processors.
Review URL: http://codereview.chromium.org/203016

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2870 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
sgjesse@chromium.org 2009-09-10 12:59:01 +00:00
parent 158dcbc39d
commit 9d2d9c596c
4 changed files with 85 additions and 14 deletions

View File

@ -157,6 +157,9 @@ void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
for (int i = 0; i < instruction_count; i++) {
*(pc_ + i) = *(instructions + i);
}
// Indicate that code has changed.
CPU::FlushICache(pc_, instruction_count);
}
@ -164,12 +167,24 @@ void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
// Additional guard int3 instructions can be added if required.
void RelocInfo::PatchCodeWithCall(Address target, int guard_bytes) {
// Call instruction takes up 5 bytes and int3 takes up one byte.
int code_size = 5 + guard_bytes;
static const int kCallCodeSize = 5;
int code_size = kCallCodeSize + guard_bytes;
// Create a code patcher.
CodePatcher patcher(pc_, code_size);
// Add a label for checking the size of the code used for returning.
#ifdef DEBUG
Label check_codesize;
patcher.masm()->bind(&check_codesize);
#endif
// Patch the code.
CodePatcher patcher(pc_, code_size);
patcher.masm()->call(target, RelocInfo::NONE);
// Check that the size of the code generated is as expected.
ASSERT_EQ(kCallCodeSize, patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
// Add the requested number of int3 instructions after the call.
for (int i = 0; i < guard_bytes; i++) {
patcher.masm()->int3();

View File

@ -173,22 +173,32 @@ void CpuFeatures::Probe() {
// 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.
static const int kCallInstructionSize = 13;
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;
// Load register with immediate 64 and call through a register instructions
// takes up 13 bytes and int3 takes up one byte.
static const int kCallCodeSize = 13;
int code_size = kCallCodeSize + guard_bytes;
// Create a code patcher.
CodePatcher patcher(pc_, code_size);
// Add a label for checking the size of the code used for returning.
#ifdef DEBUG
Label check_codesize;
patcher.masm()->bind(&check_codesize);
#endif
// Patch the code.
patcher.masm()->movq(r10, target, RelocInfo::NONE);
patcher.masm()->call(r10);
// Check that the size of the code generated is as expected.
ASSERT_EQ(kCallCodeSize,
patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
// Add the requested number of int3 instructions after the call.
for (int i = 0; i < guard_bytes; i++) {
*(patch_site + kCallInstructionSize + i) = 0xCC; // int3
patcher.masm()->int3();
}
// Indicate that code has changed.
CPU::FlushICache(patch_site, kCallInstructionSize + guard_bytes);
}
@ -197,6 +207,9 @@ void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
for (int i = 0; i < instruction_count; i++) {
*(pc_ + i) = *(instructions + i);
}
// Indicate that code has changed.
CPU::FlushICache(pc_, instruction_count);
}
// -----------------------------------------------------------------------------

View File

@ -2087,4 +2087,25 @@ void MacroAssembler::UndoAllocationInNewSpace(Register object) {
}
#ifdef ENABLE_DEBUGGER_SUPPORT
CodePatcher::CodePatcher(byte* address, int size)
: address_(address), size_(size), masm_(address, size + Assembler::kGap) {
// Create a new macro assembler pointing to the address of the code to patch.
// The size is adjusted with kGap on order for the assembler to generate size
// bytes of instructions without failing with buffer size constraints.
ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
}
CodePatcher::~CodePatcher() {
// Indicate that code has changed.
CPU::FlushICache(address_, size_);
// Check that the code was patched as expected.
ASSERT(masm_.pc_ == address_ + size_);
ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
}
#endif // ENABLE_DEBUGGER_SUPPORT
} } // namespace v8::internal

View File

@ -589,6 +589,28 @@ class MacroAssembler: public Assembler {
};
#ifdef ENABLE_DEBUGGER_SUPPORT
// The code patcher is used to patch (typically) small parts of code e.g. for
// debugging and other types of instrumentation. When using the code patcher
// the exact number of bytes specified must be emitted. Is not legal to emit
// relocation information. If any of these constraints are violated it causes
// an assertion.
class CodePatcher {
public:
CodePatcher(byte* address, int size);
virtual ~CodePatcher();
// Macro assembler to emit code.
MacroAssembler* masm() { return &masm_; }
private:
byte* address_; // The address of the code being patched.
int size_; // Number of bytes of the expected patch size.
MacroAssembler masm_; // Macro assembler used to generate the code.
};
#endif // ENABLE_DEBUGGER_SUPPORT
// -----------------------------------------------------------------------------
// Static helper functions.