blob: 2184ccd4f5f03a1b4809bf45fc9b3610be560e8c [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"
#if defined(DART_DYNAMIC_MODULES)
#include "vm/constants_kbc.h"
#endif
#include "vm/cpu.h"
#include "vm/instructions.h"
#include "vm/object.h"
#include "vm/virtual_memory.h"
namespace dart {
#if defined(DART_HOST_OS_MACOS) || defined(DART_HOST_OS_MACOS_IOS)
// On iOS even with debugger attached we must still guarantee that memory
// is never executable and writable at the same time. On Mac OS X
// com.apple.security.cs.allow-jit entitlement allows WX memory regions to be
// created - but we should not rely on this entitlement to be present.
static constexpr bool kShouldWriteProtectCodeByDefault = true;
#else
static constexpr bool kShouldWriteProtectCodeByDefault = false;
#endif
DEFINE_FLAG(bool,
write_protect_code,
kShouldWriteProtectCodeByDefault,
"Write protect jitted code");
#if defined(TARGET_ARCH_IA32)
WritableInstructionsScope::WritableInstructionsScope(uword address,
intptr_t size)
: address_(address), size_(size) {
if (FLAG_write_protect_code) {
VirtualMemory::Protect(reinterpret_cast<void*>(address), size,
VirtualMemory::kReadWrite);
}
}
WritableInstructionsScope::~WritableInstructionsScope() {
if (FLAG_write_protect_code) {
VirtualMemory::WriteProtectCode(reinterpret_cast<void*>(address_), size_);
}
}
#endif // defined(TARGET_ARCH_IA32)
bool MatchesPattern(uword end, const int16_t* pattern, intptr_t size) {
// When breaking within generated code in GDB, it may overwrite individual
// instructions with trap instructions, which can cause this test to fail.
//
// Ignoring trap instructions would work well enough within GDB alone, but it
// doesn't work in RR, because the check for the trap instruction itself will
// cause replay to diverge from the original record.
if (FLAG_support_rr) return true;
uint8_t* bytes = reinterpret_cast<uint8_t*>(end - size);
for (intptr_t i = 0; i < size; i++) {
int16_t val = pattern[i];
if ((val >= 0) && (val != bytes[i])) {
return false;
}
}
return true;
}
#if !defined(PRODUCT) && defined(DART_DYNAMIC_MODULES)
uint32_t BytecodePatcher::AddBreakpointAt(uword return_address,
const Bytecode& bytecode) {
auto thread = Thread::Current();
uint32_t old_opcode;
thread->isolate_group()->RunWithStoppedMutators([&]() {
old_opcode =
AddBreakpointAtWithMutatorsStopped(thread, return_address, bytecode);
});
return old_opcode;
}
void BytecodePatcher::RemoveBreakpointAt(uword return_address,
const Bytecode& bytecode,
uint32_t opcode) {
auto thread = Thread::Current();
thread->isolate_group()->RunWithStoppedMutators([&]() {
RemoveBreakpointAtWithMutatorsStopped(thread, return_address, bytecode,
opcode);
});
}
KBCInstr* GetInstructionBefore(const Bytecode& bytecode, uword return_address) {
ASSERT(bytecode.ContainsInstructionAt(return_address));
ASSERT(return_address != bytecode.PayloadStart());
uword prev = bytecode.PayloadStart();
uword current = KernelBytecode::Next(prev);
while (current < return_address) {
prev = current;
current = KernelBytecode::Next(prev);
}
ASSERT_EQUAL(current, return_address);
return reinterpret_cast<KBCInstr*>(prev);
}
uint32_t BytecodePatcher::AddBreakpointAtWithMutatorsStopped(
Thread* thread,
uword return_address,
const Bytecode& bytecode) {
auto* const instr = GetInstructionBefore(bytecode, return_address);
uint32_t old_opcode = *instr;
*instr = KernelBytecode::BreakpointOpcode(instr);
return old_opcode;
}
void BytecodePatcher::RemoveBreakpointAtWithMutatorsStopped(
Thread* thread,
uword return_address,
const Bytecode& bytecode,
uint32_t opcode) {
auto* const instr = GetInstructionBefore(bytecode, return_address);
// Must be previously enabled and not yet removed.
ASSERT(*instr == KernelBytecode::BreakpointOpcode(
static_cast<KernelBytecode::Opcode>(opcode)));
*instr = opcode;
}
#endif // !defined(PRODUCT) && defined(DART_DYNAMIC_MODULES)
} // namespace dart