// 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" #include "vm/virtual_memory.h" namespace dart { DEFINE_FLAG(bool, write_protect_code, true, "Write protect jitted code"); WritableInstructionsScope::WritableInstructionsScope(uword address, intptr_t size) : address_(address), size_(size) { if (FLAG_write_protect_code) { bool status = VirtualMemory::Protect(reinterpret_cast(address), size, VirtualMemory::kReadWrite); ASSERT(status); } } WritableInstructionsScope::~WritableInstructionsScope() { if (FLAG_write_protect_code) { bool status = VirtualMemory::Protect(reinterpret_cast(address_), size_, VirtualMemory::kReadExecute); ASSERT(status); } } static void SwapCode(intptr_t num_bytes, char* code, char* buffer) { uword code_address = reinterpret_cast(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, code); ASSERT(!jmp_entry.IsValid()); const uword patch_buffer = code.GetPatchCodePc(); ASSERT(patch_buffer != 0); JumpPattern jmp_patch(patch_buffer, code); ASSERT(jmp_patch.IsValid()); const uword jump_target = jmp_patch.TargetAddress(); intptr_t length = jmp_patch.pattern_length_in_bytes(); { WritableInstructionsScope writable_code(patch_addr, length); WritableInstructionsScope writable_buffer(patch_buffer, length); SwapCode(jmp_patch.pattern_length_in_bytes(), reinterpret_cast(patch_addr), reinterpret_cast(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, code); 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, code); ASSERT(!jmp_patch.IsValid()); intptr_t length = jmp_patch.pattern_length_in_bytes(); { WritableInstructionsScope writable_code(patch_addr, length); WritableInstructionsScope writable_buffer(patch_buffer, length); SwapCode(jmp_patch.pattern_length_in_bytes(), reinterpret_cast(patch_addr), reinterpret_cast(patch_buffer)); ASSERT(jmp_patch.IsValid()); jmp_patch.SetTargetAddress(jump_target); } } bool CodePatcher::IsEntryPatched(const Code& code) { const uword patch_addr = code.GetPcForDeoptId(Isolate::kNoDeoptId, PcDescriptors::kEntryPatch); if (patch_addr == 0) { return false; } JumpPattern jmp_entry(patch_addr, code); return jmp_entry.IsValid(); } 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 false; } JumpPattern jmp_entry(patch_addr, code); 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