blob: 986ade43e013f948788854aa64cffbe7056db0ef [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/code_patcher.h"
#include "vm/cpu.h"
#include "vm/instructions.h"
#include "vm/object.h"
namespace dart {
static void SwapCode(intptr_t num_bytes, char* code, char* buffer) {
uword code_address = reinterpret_cast<uword>(code);
for (intptr_t i = 0; i < num_bytes; i++) {
char tmp = *code;
*code = *buffer;
*buffer = tmp;
code++;
buffer++;
}
CPU::FlushICache(code_address, num_bytes);
// The buffer is not executed. No need to flush.
}
// The patch code buffer contains the jmp code which will be inserted at
// entry point.
void CodePatcher::PatchEntry(const Code& code) {
const uword patch_addr = code.GetPcForDeoptId(Isolate::kNoDeoptId,
PcDescriptors::kEntryPatch);
ASSERT(patch_addr != 0);
JumpPattern jmp_entry(patch_addr);
ASSERT(!jmp_entry.IsValid());
const uword patch_buffer = code.GetPatchCodePc();
ASSERT(patch_buffer != 0);
JumpPattern jmp_patch(patch_buffer);
ASSERT(jmp_patch.IsValid());
const uword jump_target = jmp_patch.TargetAddress();
SwapCode(jmp_patch.pattern_length_in_bytes(),
reinterpret_cast<char*>(patch_addr),
reinterpret_cast<char*>(patch_buffer));
jmp_entry.SetTargetAddress(jump_target);
}
// The entry point is a jmp instruction, the patch code buffer contains
// original code, the entry point contains the jump instruction.
void CodePatcher::RestoreEntry(const Code& code) {
const uword patch_addr = code.GetPcForDeoptId(Isolate::kNoDeoptId,
PcDescriptors::kEntryPatch);
ASSERT(patch_addr != 0);
JumpPattern jmp_entry(patch_addr);
ASSERT(jmp_entry.IsValid());
const uword jump_target = jmp_entry.TargetAddress();
const uword patch_buffer = code.GetPatchCodePc();
ASSERT(patch_buffer != 0);
// 'patch_buffer' contains original entry code.
JumpPattern jmp_patch(patch_buffer);
ASSERT(!jmp_patch.IsValid());
SwapCode(jmp_patch.pattern_length_in_bytes(),
reinterpret_cast<char*>(patch_addr),
reinterpret_cast<char*>(patch_buffer));
ASSERT(jmp_patch.IsValid());
jmp_patch.SetTargetAddress(jump_target);
}
bool CodePatcher::CodeIsPatchable(const Code& code) {
const uword patch_addr = code.GetPcForDeoptId(Isolate::kNoDeoptId,
PcDescriptors::kEntryPatch);
// kEntryPatch may not exist which means the function is not patchable.
if (patch_addr == 0) {
return true;
}
JumpPattern jmp_entry(patch_addr);
if (code.Size() < (jmp_entry.pattern_length_in_bytes() * 2)) {
return false;
}
const uword limit = patch_addr + jmp_entry.pattern_length_in_bytes();
// Check no object stored between patch_addr .. limit.
for (intptr_t i = 0; i < code.pointer_offsets_length(); i++) {
const uword obj_start = code.GetPointerOffsetAt(i) + code.EntryPoint();
const uword obj_end = obj_start + kWordSize;
if ((obj_start < limit) && (obj_end > patch_addr)) {
return false;
}
}
return true;
}
} // namespace dart