GitLab will continue to be upgraded from 11.4.5-ce.0 on November 25th 2019 at 4.00pm (AEDT) to 5.00pm (AEDT) due to Critical Security Patch Availability. During the update, GitLab and Mattermost services will not be available.

Commit 9296466a authored by qinsoon's avatar qinsoon

implemented wpbranch

parent 2f973136
Pipeline #1111 passed with stages
in 44 minutes and 57 seconds
......@@ -38,4 +38,8 @@ pub fn spill_rewrite(
pub fn jit_patch_call_addr(dest: Address, inst_start: Address, length: ByteSize) {
unimplemented!()
}
pub fn jit_patch_jmp_addr(dest: Address, inst_start: Address, length: ByteSize) {
unimplemented!()
}
\ No newline at end of file
......@@ -3232,6 +3232,16 @@ impl CodeGenerator for ASMCodeGen {
self.add_asm_branch(asm, dest_name)
}
fn emit_wpbranch_jmp(
&mut self,
wpid: MuID,
target: MuName,
disable_target: MuName,
enable_target: MuName
) {
panic!("cannot emit wpbranch_jmp with asm backend (requires runtime code patching)")
}
fn emit_je(&mut self, dest_name: MuName) {
trace!("emit: je {}", dest_name);
......
......@@ -352,6 +352,32 @@ impl BinaryCodeGen {
)
}
fn add_xed_wpbranch(
&mut self,
inst: XedInstruction,
wpid: MuID,
target: MuName,
disable_target: MuName,
enable_target: MuName
) {
self.add_xed_inst_internal(
inst,
linked_hashmap!{},
linked_hashmap!{},
None,
false,
false,
None,
BranchTarget::WPBranch {
wpid: wpid,
target: target,
disable_target: disable_target,
enable_target: enable_target
},
None
)
}
fn add_xed_inst(
&mut self,
inst: XedInstruction,
......@@ -1179,6 +1205,33 @@ impl CodeGenerator for BinaryCodeGen {
self.add_xed_branch(inst, dest)
}
fn emit_wpbranch_jmp(
&mut self,
wpid: MuID,
default_target: MuName,
disable_target: MuName,
enable_target: MuName
) {
trace!(
"emit wpbranch #{} jmp {} (dis: {}, ena: {})",
wpid,
default_target,
disable_target,
enable_target
);
let target = xed_relbr(0, 32);
let inst = xed_inst1(
self.cur().state,
xed_iclass_enum_t::XED_ICLASS_JMP,
64,
target
);
self.add_xed_wpbranch(inst, wpid, default_target, disable_target, enable_target);
}
fn emit_je(&mut self, dest: MuName) {
unimplemented!()
}
......@@ -1556,6 +1609,7 @@ pub fn emit_code(fv: &mut MuFunctionVersion, vm: &VM) {
pub fn jit_patch_call_addr(dest: Address, inst_start: Address, length: ByteSize) {
use std::i32;
info!("patch call instruction at {} to call {}", inst_start, dest);
let call_offset = dest.diff_offset(inst_start + length);
......@@ -1586,3 +1640,37 @@ pub fn jit_patch_call_addr(dest: Address, inst_start: Address, length: ByteSize)
// do nothing
}
}
pub fn jit_patch_jmp_addr(dest: Address, inst_start: Address, length: ByteSize) {
use std::i32;
info!("patch jump instruction at {} to jmp {}", inst_start, dest);
let jmp_offset = dest.diff_offset(inst_start + length);
assert!(
jmp_offset >= i32::MIN as isize && jmp_offset <= i32::MAX as isize,
"jmp offset is not suitable for a jmp"
);
let inst = xed_inst1(
xed_state_64(),
xed_iclass_enum_t::XED_ICLASS_JMP,
64,
xed_relbr(jmp_offset as i32, 32)
);
let new_len = encode_inst(
&inst,
inst_start.to_ptr_mut(),
XED_MAX_INSTRUCTION_BYTES as usize
);
if new_len < length {
warn!("need to pad with a few nops");
unimplemented!();
} else if new_len > length {
panic!("patching used more space than we have");
} else {
// do nothing
}
}
......@@ -51,7 +51,7 @@ pub struct XedContext {
pub insts_addr: Vec<Address>,
pub insts_len: Vec<ByteSize>,
pub callsite_addr: HashMap<MuName, Address>
pub symbol_addr: HashMap<MuName, Address>
}
impl XedContext {
......@@ -65,7 +65,7 @@ impl XedContext {
frame_size_patchpoints: vec![],
insts_addr: vec![],
insts_len: vec![],
callsite_addr: HashMap::new()
symbol_addr: HashMap::new()
}
}
......@@ -280,7 +280,7 @@ impl XedContext {
pub fn generate_code(&mut self, code_ptr: Address, len: ByteSize, vm: &VM) -> ByteSize {
info!("code mission");
// generate code and recording branches and block start address for back patching
// generate code and recording callsite symbol-address mapping for back patching
let code_length = {
let mut cur_ptr = code_ptr;
let mut used = 0;
......@@ -308,7 +308,7 @@ impl XedContext {
match self.insts_info[i].call {
Some(JITCallsite { ref name, .. }) => {
self.callsite_addr.insert(name.clone(), cur_ptr);
self.symbol_addr.insert(name.clone(), cur_ptr);
}
None => {}
}
......@@ -322,19 +322,52 @@ impl XedContext {
used
};
// generate a mapping between block name and address
{
for (blk_name, blk_info) in self.blocks.iter() {
let addr = self.insts_addr[blk_info.start_inst];
self.symbol_addr.insert(blk_name.clone(), addr);
}
}
// another pass through the code, record patchpoints
info!("record patch point...");
for i in 0..self.insts.len() {
let inst_addr = self.insts_addr[i];
let inst_len = self.insts_len[i];
// add patchpoint for callsite
if let Some(ref callsite) = self.insts_info[i].call {
let inst_addr = self.insts_addr[i];
let patchpoint = RuntimePatchpoint::inst(inst_addr, self.insts_len[i]);
let patchpoint = RuntimePatchpoint::call_inst(inst_addr, inst_len);
trace!(
"added callsite for func {} at {}",
"added callsite patchpoint for func {}: {:?}",
callsite.callee_id,
inst_addr
patchpoint
);
vm.add_callsite_patchpoint(callsite.callee_id, patchpoint);
}
// add patchpoint for wpbranch
match self.insts_info[i].branch {
// we dont care which target we actually jump to at the moment
// we only record information
BranchTarget::WPBranch {
wpid,
ref disable_target,
ref enable_target,
..
} => {
let disable_addr = *self.symbol_addr.get(disable_target).unwrap();
let enable_addr = *self.symbol_addr.get(enable_target).unwrap();
// add a wpbranch patchpoint for the watchpoint
let patchpoint =
RuntimePatchpoint::wpbranch(inst_addr, inst_len, disable_addr, enable_addr);
trace!("added wpbranch patchpoint for {}: {:?}", wpid, patchpoint);
vm.add_wpbranch_patchpoint(wpid, patchpoint);
}
_ => {}
}
}
info!("back patching...");
......@@ -421,7 +454,8 @@ impl XedContext {
// we may need to patch offsets
if let Some(new_inst) = match info.branch {
BranchTarget::Unconditional(ref target) => {
BranchTarget::Unconditional(ref target) |
BranchTarget::WPBranch { ref target, .. } => {
let block_start = self.get_block_range(target).unwrap().start;
// the target address we need to jump to
let target_addr = self.insts_addr[block_start];
......@@ -429,7 +463,7 @@ impl XedContext {
let branch_offset = target_addr.diff_offset(cur_ip);
trace!(
"unconditional branch to inst {} of 0x{:x}, with offset {}",
"unconditional branch/wpbranch to inst {} of 0x{:x}, with offset {}",
block_start,
target_addr,
branch_offset
......@@ -482,7 +516,7 @@ impl XedContext {
// we need to resolve the address for the label
// check if it is a local callsite label
if let Some(label_addr) = self.callsite_addr.get(name) {
if let Some(label_addr) = self.symbol_addr.get(name) {
patch_disp(patchpoint, *label_addr)
} else {
let addr = resolve_symbol(name.clone());
......@@ -699,6 +733,71 @@ impl XedContext {
panic!("PEI does not have a fallthrough target");
}
}
BranchTarget::WPBranch {
wpid,
ref disable_target,
ref enable_target,
..
} => {
// we may jump to default target or alt target
// branch to default
{
let target_n = self.blocks.get(disable_target).unwrap().start_inst;
// cur inst's succ is target
code[i].succs.push(target_n);
// target's pred is cur
code[target_n].preds.push(i);
trace_if!(
TRACE_CFA,
"inst {}: is a wpbranch with default branch to {}",
i,
target_n
);
trace_if!(TRACE_CFA, "inst {}: branch target index is {}", i, target_n);
trace_if!(
TRACE_CFA,
"inst {}: set SUCCS as branch target {}",
i,
target_n
);
trace_if!(
TRACE_CFA,
"inst {}: set PREDS as branch source {}",
target_n,
i
);
}
// branch to alternative
{
let target_n = self.blocks.get(enable_target).unwrap().start_inst;
// cur inst's succ is target
code[i].succs.push(target_n);
// target's pred is cur
code[target_n].preds.push(i);
trace_if!(
TRACE_CFA,
"inst {}: is a wpbranch with default branch to {}",
i,
target_n
);
trace_if!(TRACE_CFA, "inst {}: branch target index is {}", i, target_n);
trace_if!(
TRACE_CFA,
"inst {}: set SUCCS as branch target {}",
i,
target_n
);
trace_if!(
TRACE_CFA,
"inst {}: set PREDS as branch source {}",
target_n,
i
);
}
}
BranchTarget::Return => {
trace_if!(TRACE_CFA, "inst {}: is a return", i);
trace_if!(TRACE_CFA, "inst {}: has no successor", i);
......@@ -1125,6 +1224,13 @@ pub enum BranchTarget {
Unconditional(MuName),
/// this instruction may throw exception to target
PotentiallyExcepting(MuName),
/// this instruction branch to default target, but may branch to alt target
WPBranch {
wpid: MuID,
target: MuName,
disable_target: MuName,
enable_target: MuName
},
/// this instruction is a return
Return
}
......
......@@ -20,6 +20,7 @@ pub use self::binary_codegen::BinaryCodeGen;
pub use self::binary_codegen::init_jit;
pub use self::binary_codegen::emit_code;
pub use self::binary_codegen::jit_patch_call_addr;
pub use self::binary_codegen::jit_patch_jmp_addr;
mod code_context;
......
......@@ -224,6 +224,14 @@ pub trait CodeGenerator {
fn emit_jle(&mut self, dest: MuName);
fn emit_js(&mut self, dest: MuName);
fn emit_wpbranch_jmp(
&mut self,
wpid: MuID,
target: MuName,
disable_target: MuName,
enable_target: MuName
);
// call
fn emit_call_near_rel32(
&mut self,
......
......@@ -2175,6 +2175,7 @@ impl<'a> InstructionSelection {
vm
);
}
Instruction_::Watchpoint {
ref id,
ref disable_dest,
......@@ -2188,6 +2189,30 @@ impl<'a> InstructionSelection {
self.emit_trap(node, inst, resume, f_content, f_context, vm);
}
}
Instruction_::WPBranch {
wp,
ref disable_dest,
ref enable_dest
} => {
trace!("instsel on WPBRANCH");
let ref ops = inst.ops;
self.process_dest(&ops, disable_dest, f_content, f_context, vm);
self.process_dest(&ops, enable_dest, f_content, f_context, vm);
let disable_target = f_content.get_block(disable_dest.target).name();
let enable_target = f_content.get_block(enable_dest.target).name();
vm.ensure_watchpoint_registered(wp);
let default_target = if vm.is_watchpoint_enabled(wp) {
enable_target.clone()
} else {
disable_target.clone()
};
self.backend
.emit_wpbranch_jmp(wp, default_target, disable_target, enable_target);
}
Instruction_::PrintHex(index) => {
trace!("instsel on PRINTHEX");
......
......@@ -135,6 +135,9 @@ pub use compiler::backend::x86_64::binary_backend::spill_rewrite as binary_spill
#[cfg(feature = "binary-codegen")]
pub use compiler::backend::x86_64::binary_backend::jit_patch_call_addr;
#[cfg(target_arch = "x86_64")]
#[cfg(feature = "binary-codegen")]
pub use compiler::backend::x86_64::binary_backend::jit_patch_jmp_addr;
#[cfg(target_arch = "x86_64")]
pub use compiler::backend::x86_64::ARGUMENT_GPRS;
#[cfg(target_arch = "x86_64")]
pub use compiler::backend::x86_64::ARGUMENT_FPRS;
......@@ -226,6 +229,9 @@ pub use compiler::backend::aarch64::binary_backend::spill_rewrite as binary_spil
#[cfg(feature = "binary-codegen")]
pub use compiler::backend::aarch64::binary_backend::jit_patch_call_addr;
#[cfg(target_arch = "aarch64")]
#[cfg(feature = "binary-codegen")]
pub use compiler::backend::aarch64::binary_backend::jit_patch_jmp_addr;
#[cfg(target_arch = "aarch64")]
pub use compiler::backend::aarch64::ARGUMENT_GPRS;
#[cfg(target_arch = "aarch64")]
pub use compiler::backend::aarch64::ARGUMENT_FPRS;
......
......@@ -15,6 +15,7 @@
extern crate hprof;
use ast::ir::*;
use runtime::trap::Watchpoint;
use vm::VM;
/// compiler passes
......@@ -196,12 +197,35 @@ pub fn update_callsite_in_old_code(func_id: MuID, func_start: Address, vm: &VM)
use runtime::patchpoint::*;
let patch_table = vm.callsite_patch_table().read().unwrap();
for patchpoint in patch_table.get(&func_id).unwrap().iter() {
assert!(patchpoint.v == RuntimePatchpoint_::Instruction);
assert!(patchpoint.v == RuntimePatchpoint_::CallInst);
backend::jit_patch_call_addr(func_start, patchpoint.start, patchpoint.length);
}
}
pub fn patch_watchpoint(to_enable: bool, wp: &Watchpoint) {
use runtime::patchpoint::*;
// patch for wpbranch inst
for patchpoint in wp.wpbranch_patchpoints.iter() {
match patchpoint.v {
RuntimePatchpoint_::WPBranch {
disable_dest,
enable_dest
} => {
if to_enable {
backend::jit_patch_jmp_addr(enable_dest, patchpoint.start, patchpoint.length);
} else {
backend::jit_patch_jmp_addr(disable_dest, patchpoint.start, patchpoint.length);
}
}
_ => panic!("expected wpbranch patchpoint, found {:?}", patchpoint)
}
}
// TODO: patch for watchpoint inst
}
// rewrite parts of the hprof crates to print via log (instead of print!())
use self::hprof::ProfileNode;
use std::rc::Rc;
......
......@@ -264,7 +264,44 @@ impl CompilerPass for GenMovPhi {
trace!("rewrite to {}", new_inst);
new_body.push(new_inst);
}
Instruction_::WPBranch { .. } => unimplemented!(),
Instruction_::WPBranch {
wp,
disable_dest,
enable_dest
} => {
let disable_dest = process_dest(
disable_dest,
&mut new_blocks_to_insert,
&ops,
vm,
&inst_name,
"wpbranch_disable"
);
let enable_dest = process_dest(
enable_dest,
&mut new_blocks_to_insert,
&ops,
vm,
&inst_name,
"wpbranch_enable"
);
// rewrite the instruction
let new_inst = func.new_inst(Instruction {
hdr: inst.hdr.clone(),
value: inst.value.clone(),
ops: ops.to_vec(),
keepalives: inst.keepalives.clone(),
v: Instruction_::WPBranch {
wp: wp,
disable_dest: disable_dest,
enable_dest: enable_dest
}
});
trace!("rewrite to {}", new_inst);
new_body.push(new_inst);
}
Instruction_::SwapStackExc {
stack,
is_exception,
......
......@@ -12,17 +12,37 @@ pub struct RuntimePatchpoint {
rodal_named!(RuntimePatchpoint);
impl RuntimePatchpoint {
pub fn inst(start: Address, length: ByteSize) -> RuntimePatchpoint {
pub fn call_inst(start: Address, length: ByteSize) -> RuntimePatchpoint {
RuntimePatchpoint {
start: start,
length: length,
v: RuntimePatchpoint_::Instruction
v: RuntimePatchpoint_::CallInst
}
}
pub fn wpbranch(
start: Address,
length: ByteSize,
disable_dest: Address,
enable_dest: Address
) -> RuntimePatchpoint {
RuntimePatchpoint {
start: start,
length: length,
v: RuntimePatchpoint_::WPBranch {
disable_dest: disable_dest,
enable_dest: enable_dest
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum RuntimePatchpoint_ {
Instruction,
CallInst,
WPBranch {
disable_dest: Address,
enable_dest: Address
},
Offset(ByteSize)
}
\ No newline at end of file
......@@ -4,6 +4,7 @@ use vm::api::prepare_handle;
use vm::api::api_c::*;
use vm::handle::*;
use runtime::thread::MuThread;
use runtime::patchpoint::RuntimePatchpoint;
use utils::Address;
use utils::WORD_SIZE;
......@@ -197,3 +198,10 @@ pub extern "C" fn muentry_trap(wpid: MuID, arg_loc: Address, stack_arg_loc: Addr
_ => panic!("expected trap result: {}", ret_trap_result)
}
}
pub struct Watchpoint {
pub enabled: bool,
pub wpbranch_patchpoints: Vec<RuntimePatchpoint>
}
rodal_named!(Watchpoint);
\ No newline at end of file
......@@ -36,6 +36,7 @@ use runtime::*;
use runtime::mm as gc;
use runtime::patchpoint::RuntimePatchpoint;
use runtime::trap::TrapHandler;
use runtime::trap::Watchpoint;
use runtime::thread::{FrameCursor, FrameCursorStepResult};
use vm::handle::*;
use vm::vm_options::VMOptions;
......@@ -138,6 +139,11 @@ pub struct VM {
/// We keep this as a separate table because we want to index it with function ID
callsite_patchpoints: RwLock<HashMap<MuID, Vec<RuntimePatchpoint>>>,
/// watchpoints in current VM. This serves for multiple uses:
/// * track status of watchpoints (enabled or disabled)
/// * track patchpoints for WPBranch
watchpoints: RwLock<HashMap<WPID, Watchpoint>>,
/// registered trap handler for the client
trap_handler: RwLock<Option<TrapHandler>>,
......@@ -197,6 +203,10 @@ unsafe impl rodal::Dump for VM {
RwLock::new(rodal::EmptyHashMap::<MuID, Vec<RuntimePatchpoint>>::new());
dumper.dump_object_here(&callsite_patchpoints);
dumper.dump_padding(&self.watchpoints);
let watchpoints = RwLock::new(rodal::EmptyHashMap::<WPID, Watchpoint>::new());
dumper.dump_object_here(&watchpoints);
dumper.dump_padding(&self.trap_handler);
let empty_handler = RwLock::new(rodal::EmptyOption::<TrapHandler>::new());
dumper.dump_object_here(&empty_handler);
......@@ -282,6 +292,7 @@ impl<'a> VM {
compiled_callsite_table: RwLock::new(HashMap::new()),
callsite_count: ATOMIC_USIZE_INIT,
callsite_patchpoints: RwLock::new(HashMap::new()),
watchpoints: RwLock::new(HashMap::new()),
trap_handler: RwLock::new(None),
pending_joins: Mutex::new(LinkedList::new()),
framecursors: Mutex::new(HashMap::new())
......@@ -335,6 +346,7 @@ impl<'a> VM {
aot_pending_funcref_store: RwLock::new(HashMap::new()),
compiled_callsite_table: RwLock::new(HashMap::new()),
callsite_patchpoints: RwLock::new(HashMap::new()),
wpbranch_patchpoints: RwLock::new(HashMap::new()),
trap_handler: RwLock::new(None),
callsite_count: ATOMIC_USIZE_INIT,
pending_joins: Mutex::new(LinkedList::new()),
......@@ -493,6 +505,96 @@ impl<'a> VM {
}
}
/// registers a watchpoint
pub fn ensure_watchpoint_registered(&self, wpid: WPID) {
let mut table = self.watchpoints.write().unwrap();
if table.contains_key(&wpid) {
warn!("watchpoint {} was registered, do nothing", wpid);
} else {
info!("registering watchpoint {}", wpid);
table.insert(
wpid,
Watchpoint {
enabled: false,
wpbranch_patchpoints: vec![]
}
);
}
}
/// adds a wpbranch patchpoint
pub fn add_wpbranch_patchpoint(&self, wpid: WPID, patchpoint: RuntimePatchpoint) {
let mut table = self.watchpoints.write().unwrap();
if table.contains_key(&wpid) {
let mut wp = table.get_mut(&wpid).unwrap();
wp.wpbranch_patchpoints.push(patchpoint);
} else {
// we do not have a watchpoint for it yet
panic!(
"trying to add a patchpoint for wpbranch, \
but the watchpoint {} is not registered yet",
wpid
)
}
}
/// checks if a watchpoint is enabled
pub fn is_watchpoint_enabled(&self, wpid: WPID) -> bool {
// it is possible that we haven't compile the code yet
// thus we do not know about the watchpoint
self.ensure_watchpoint_registered(wpid);
let table = self.watchpoints.read().unwrap();
if table.contains_key(&wpid) {
table.get(&wpid).unwrap().enabled
} else {
panic!("watchpoint {} is not registered", wpid)
}
}
/// enables a watchpoint
pub fn enable_watchpoint(&self, wpid: WPID) {
info!("enabling watchpoint {}", wpid);
// it is possible that we haven't compile the code yet
// thus we do not know about the watchpoint
self.ensure_watchpoint_registered(wpid);
let mut table = self.watchpoints.write().unwrap();
match table.get_mut(&wpid) {
Some(ref mut wp) => {
assert!(!wp.enabled);
wp.enabled = true;
compiler::patch_watchpoint(true, wp);
}
None => unreachable!()
}
}
/// disables a watchpoint
pub fn disable_watchpoint(&self, wpid: MuID) {
info!("disabling watchpoint {}", wpid);
// it is possible that we haven't compile the code yet
// thus we do not know about the watchpoint
self.ensure_watchpoint_registered(wpid);