diff --git a/.gitignore b/.gitignore index d39826f3..6ad9b1bb 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ **/*.dot **/*.png **/*.s +.DS_Store diff --git a/Cargo.lock b/Cargo.lock index c3e51248..1a44953f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,9 +65,9 @@ checksum = "7b02b629252fe8ef6460461409564e2c21d0c8e77e0944f3d189ff06c4e932ad" [[package]] name = "cc" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c" +checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" [[package]] name = "cfg-if" @@ -155,9 +155,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" [[package]] name = "heck" @@ -170,9 +170,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" dependencies = [ "libc", ] @@ -195,9 +195,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.77" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" +checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" [[package]] name = "lock_api" @@ -339,9 +339,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.21" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ "unicode-xid", ] @@ -405,7 +405,7 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "riscv-decode" version = "0.2.0" -source = "git+https://github.com/cksystemsgroup/riscv-decode#3439f507fb0fa3eb76f7370553ba079f9a888988" +source = "git+https://github.com/cksystemsgroup/riscv-decode#f5720e8fd07d2ec7bfa9fc6746b052e908a1f8ac" [[package]] name = "scopeguard" @@ -415,18 +415,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "scroll" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1" +checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" dependencies = [ "scroll_derive", ] [[package]] name = "scroll_derive" -version = "0.10.2" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e367622f934864ffa1c704ba2b82280aab856e3d8213c84c5720257eb34b15b9" +checksum = "b12bd20b94c7cdfda8c7ba9b92ad0d9a56e3fa018c25fca83b51aa664c9b4c0d" dependencies = [ "proc-macro2", "quote", @@ -469,9 +469,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.41" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b" +checksum = "5ad5de3220ea04da322618ded2c42233d02baca219d6f160a3e9c87cda16c942" dependencies = [ "proc-macro2", "quote", diff --git a/src/candidate_path.rs b/src/candidate_path.rs index 4d8c0dc4..d214a427 100644 --- a/src/candidate_path.rs +++ b/src/candidate_path.rs @@ -84,7 +84,7 @@ mod tests { let test_file = Path::new("symbolic/division-by-zero-3-35.riscu.o"); - let (graph, _, _) = build_from_file(test_file).unwrap(); + let (graph, _, _) = build_cfg_from_file(test_file).unwrap(); let paths = create_candidate_paths(&graph).into_iter().count(); diff --git a/src/cfg.rs b/src/cfg.rs index f844440e..5861f8a2 100644 --- a/src/cfg.rs +++ b/src/cfg.rs @@ -15,43 +15,18 @@ use crate::elf::{load_file, ElfMetadata}; use byteorder::{ByteOrder, LittleEndian}; -use petgraph::dot::Dot; -use petgraph::graph::{EdgeIndex, NodeIndex}; -use petgraph::visit::EdgeRef; -use petgraph::Graph; -use riscv_decode::decode; -use riscv_decode::Instruction; -use std::collections::HashSet; -use std::fs::File; -use std::io::prelude::*; -use std::path::Path; -use std::process::Command; -use std::vec::Vec; +use petgraph::{ + dot::Dot, + graph::{EdgeIndex, NodeIndex}, + visit::EdgeRef, + Graph, +}; +use riscv_decode::{decode, Instruction, Register}; +use std::{fs::File, io::prelude::*, path::Path, process::Command, vec::Vec}; type Edge = (NodeIndex, NodeIndex, Option); pub type ControlFlowGraph = Graph>; -/// Extend sign -pub fn sign_extend(n: u32, b: u32) -> u32 { - // assert: 0 <= n <= 2^b - // assert: 0 < b < CPUBITWIDTH - if n < 2_u32.pow(b - 1) { - n - } else { - n.wrapping_sub(2_u32.pow(b)) - } -} - -/// Get `NodeIndex` of `beq` destination. -fn calculate_beq_destination(idx: NodeIndex, imm: u32) -> NodeIndex { - NodeIndex::new(sign_extend(imm / 4, 11).wrapping_add(idx.index() as u32) as usize) -} - -/// Get `NodeIndex` of `jal` destination. -fn calculate_jal_destination(idx: NodeIndex, imm: u32) -> NodeIndex { - NodeIndex::new(sign_extend(imm / 4, 19).wrapping_add(idx.index() as u32) as usize) -} - /// Create a `ControlFlowGraph` from an `u8` slice without fixing edges fn create_instruction_graph(binary: &[u8]) -> ControlFlowGraph { binary @@ -79,10 +54,16 @@ fn construct_edge_if_trivial(graph: &ControlFlowGraph, idx: NodeIndex) -> Option /// Compute pure edges fn construct_edge_if_pure(graph: &ControlFlowGraph, idx: NodeIndex) -> Option { match graph[idx] { - Instruction::Jal(i) if i.rd() == 0 => { - Some((idx, calculate_jal_destination(idx, i.imm()), None)) - } - Instruction::Beq(i) => Some((idx, calculate_beq_destination(idx, i.imm()), None)), + Instruction::Jal(i) if i.rd() == Register::Zero => Some(( + idx, + NodeIndex::new((((idx.index() as u64) * 4).wrapping_add(i.imm() as u64) / 4) as usize), + None, + )), + Instruction::Beq(i) => Some(( + idx, + NodeIndex::new((((idx.index() as u64) * 4).wrapping_add(i.imm() as u64) / 4) as usize), + None, + )), _ => None, } } @@ -99,41 +80,45 @@ where } /// Compute all return locations in a given function starting at idx. -fn compute_return_edge_position(graph: &ControlFlowGraph, idx: NodeIndex) -> HashSet { +fn compute_return_edge_position(graph: &ControlFlowGraph, idx: NodeIndex) -> NodeIndex { match graph[idx] { - Instruction::Jalr(_) => { - let mut set = HashSet::new(); - set.insert(idx); - set - } - Instruction::Jal(i) if i.rd() != 0 => { + Instruction::Jalr(_) => idx, + Instruction::Jal(i) if i.rd() != Register::Zero => { compute_return_edge_position(graph, NodeIndex::new(idx.index() + 1)) } - _ => graph - .edges(idx) - .flat_map(|e| compute_return_edge_position(graph, e.target())) - .collect(), + Instruction::Beq(_) => compute_return_edge_position(graph, { + // second edge is the true branch edge, which jumps to the end of the loop (Selfie + graph + .edges(idx) + .filter(|e| e.target().index() != idx.index() + 1) + .next() + .unwrap() + .target() + }), + _ => compute_return_edge_position(graph, graph.edges(idx).next().unwrap().target()), } } /// Fix stateful edges and return a vector containing them fn construct_edge_if_stateful(idx: NodeIndex, graph: &ControlFlowGraph) -> Option> { match graph[idx] { - Instruction::Jal(jtype) if jtype.rd() != 0 => { + Instruction::Jal(jtype) if jtype.rd() != Register::Zero => { // jump and link => function call - let jump_dest = calculate_jal_destination(idx, jtype.imm()); + let jump_dest = NodeIndex::new( + (((idx.index() as u64) * 4).wrapping_add(jtype.imm() as u64) / 4) as usize, + ); let return_dest = NodeIndex::new(idx.index() + 1); + let mark = Some(return_dest); - let mut edges = compute_return_edge_position(graph, jump_dest) - .iter() - .map(|rp| (*rp, return_dest, Some(idx))) - .collect::>(); + let return_edge = ( + compute_return_edge_position(graph, jump_dest), + return_dest, + mark, + ); - let call_edge = (idx, jump_dest, Some(idx)); + let call_edge = (idx, jump_dest, mark); - edges.push(call_edge); - - Some(edges) + Some(vec![call_edge, return_edge]) } _ => None, } @@ -168,7 +153,7 @@ fn find_possible_exit_edge(graph: &ControlFlowGraph, idx: NodeIndex) -> Option; /// Create a ControlFlowGraph from Path `file`. // TODO: only tested with Selfie RISC-U file and relies on that ELF format -pub fn build_from_file(file: &Path) -> Result<(ControlFlowGraph, DataSegment, ElfMetadata), &str> { +pub fn build_cfg_from_file

( + file: P, +) -> Result<(ControlFlowGraph, DataSegment, ElfMetadata), &'static str> +where + P: AsRef, +{ match load_file(file, 1024) { Some((code, data, meta_data)) => Ok((build(code.as_slice()), data, meta_data)), None => Err("Cannot load RISC-U ELF file"), @@ -234,52 +224,3 @@ pub fn convert_dot_to_png(source: &Path, output: &Path) -> Result<(), &'static s Ok(()) } - -#[cfg(test)] -mod tests { - use super::*; - use serial_test::serial; - use std::env::current_dir; - use std::string::String; - - // TODO: write a unit test without dependency on selfie and external files - #[test] - #[serial] - fn can_build_control_flow_graph() { - let cd = String::from(current_dir().unwrap().to_str().unwrap()); - - // generate RISC-U binary with Selfie - let _ = Command::new("docker") - .arg("run") - .arg("-v") - .arg(cd + ":/opt/monster") - .arg("cksystemsteaching/selfie") - .arg("/opt/selfie/selfie") - .arg("-c") - .arg("/opt/monster/symbolic/division-by-zero-3-35.c") - .arg("-o") - .arg("/opt/monster/symbolic/division-by-zero-3-35.riscu.o") - .output(); - - let test_file = Path::new("symbolic/division-by-zero-3-35.riscu.o"); - - let (graph, _, _) = build_from_file(test_file).unwrap(); - - let dot_graph = Dot::with_config(&graph, &[]); - - let mut f = File::create("tmp-graph.dot").unwrap(); - f.write_fmt(format_args!("{:?}", dot_graph)).unwrap(); - - let _ = Command::new("dot") - .arg("-Tpng") - .arg("tmp-graph.dot") - .arg("-o") - .arg("main.png") - .output(); - - // TODO: test more than just this result - // assert!(result.is_ok()); - - let _ = std::fs::remove_file(test_file); - } -} diff --git a/src/cli.rs b/src/cli.rs index 7ac52522..2317336c 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -38,15 +38,6 @@ pub fn args() -> App<'static> { .takes_value(true) .value_name("FILE") .default_value("cfg.dot"), - ) - .arg( - Arg::new("format") - .about("File format of the generated CFG") - .short('f') - .long("format") - .takes_value(true) - .possible_values(&["dot", "png"]) - .default_value("dot"), ), ) .subcommand( diff --git a/src/decode.rs b/src/decode.rs index 4f8ab943..e50a7e7d 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -46,7 +46,7 @@ impl Decoder<'_, R> { Instruction::Jal(i) => self.next.jal(i), Instruction::Jalr(i) => self.next.jalr(i), Instruction::Beq(i) => self.next.beq(i), - Instruction::Ecall => self.next.ecall(), + Instruction::Ecall(_) => self.next.ecall(), i => unimplemented!("instruction: {:?}", i), } } else { diff --git a/src/disassemble.rs b/src/disassemble.rs index 1691de5e..b4fc3462 100644 --- a/src/disassemble.rs +++ b/src/disassemble.rs @@ -2,140 +2,72 @@ use crate::elf::load_file; use byteorder::{ByteOrder, LittleEndian}; -use riscv_decode::types::{BType, IType, JType, RType, SType, UType}; +use riscv_decode::{ + types::{BType, IType, JType, RType, SType, UType}, + Register, +}; use std::path::Path; use crate::decode::{Decoder, RiscU}; -fn reg_to_str(reg: u32) -> String { - match reg { - 0 => String::from("zero"), - 1 => String::from("ra"), - 2 => String::from("sp"), - 3 => String::from("gp"), - 4 => String::from("tp"), - i if i >= 5 && i <= 7 => format!("t{}", i - 5), - i if i >= 8 && i <= 9 => format!("s{}", i - 8), - i if i >= 10 && i <= 17 => format!("a{}", i - 10), - i if i >= 18 && i <= 27 => format!("s{}", i - 16), - i if i >= 28 && i <= 31 => format!("t{}", i - 25), - _ => unreachable!(), - } -} - struct Disassembler {} impl RiscU for Disassembler { fn lui(&mut self, i: UType) { - println!("lui {},{:#x}", reg_to_str(i.rd()), i.imm()) + println!("lui {:?},{:#x}", i.rd(), i.imm()) } // TODO: fix representation of negativ immediate values fn addi(&mut self, i: IType) { - if i.rd() == 0 && i.rs1() == 0 && i.imm() == 0 { + if i.rd() == Register::Zero && i.rs1() == Register::Zero && i.imm() == 0 { println!("nop") } else { - println!( - "addi {},{},{}", - reg_to_str(i.rd()), - reg_to_str(i.rs1()), - i.imm() - ) + println!("addi {:?},{:?},{}", i.rd(), i.rs1(), i.imm()) } } fn add(&mut self, i: RType) { - println!( - "add {},{},{}", - reg_to_str(i.rd()), - reg_to_str(i.rs1()), - reg_to_str(i.rs2()) - ) + println!("add {:?},{:?},{:?}", i.rd(), i.rs1(), i.rs2()) } fn sub(&mut self, i: RType) { - println!( - "sub {},{},{}", - reg_to_str(i.rd()), - reg_to_str(i.rs1()), - reg_to_str(i.rs2()) - ) + println!("sub {:?},{:?},{:?}", i.rd(), i.rs1(), i.rs2()) } fn mul(&mut self, i: RType) { - println!( - "mul {},{},{}", - reg_to_str(i.rd()), - reg_to_str(i.rs1()), - reg_to_str(i.rs2()) - ) + println!("mul {:?},{:?},{:?}", i.rd(), i.rs1(), i.rs2()) } fn divu(&mut self, i: RType) { - println!( - "divu {},{},{}", - reg_to_str(i.rd()), - reg_to_str(i.rs1()), - reg_to_str(i.rs2()) - ) + println!("divu {:?},{:?},{:?}", i.rd(), i.rs1(), i.rs2()) } fn remu(&mut self, i: RType) { - println!( - "remu {},{},{}", - reg_to_str(i.rd()), - reg_to_str(i.rs1()), - reg_to_str(i.rs2()) - ) + println!("remu {:?},{:?},{:?}", i.rd(), i.rs1(), i.rs2()) } fn sltu(&mut self, i: RType) { - println!( - "sltu {},{},{}", - reg_to_str(i.rd()), - reg_to_str(i.rs1()), - reg_to_str(i.rs2()) - ) + println!("sltu {:?},{:?},{:?}", i.rd(), i.rs1(), i.rs2()) } fn ld(&mut self, i: IType) { - println!( - "ld {},{}({})", - reg_to_str(i.rd()), - i.imm(), - reg_to_str(i.rs1()) - ) + println!("ld {:?},{}({:?})", i.rd(), i.imm(), i.rs1()) } fn sd(&mut self, i: SType) { - println!( - "sd {},{}({})", - reg_to_str(i.rs2()), - i.imm(), - reg_to_str(i.rs1()) - ) + println!("sd {:?},{}({:?})", i.rs2(), i.imm(), i.rs1()) } fn jal(&mut self, i: JType) { - println!("jal {},{}", reg_to_str(i.rd()), i.imm()) + println!("jal {:?},{}", i.rd(), i.imm()) } fn jalr(&mut self, i: IType) { - println!( - "jalr {},{}({})", - reg_to_str(i.rd()), - i.imm(), - reg_to_str(i.rs1()) - ) + println!("jalr {:?},{}({:?})", i.rd(), i.imm(), i.rs1()) } fn beq(&mut self, i: BType) { - println!( - "beq {},{},{}", - reg_to_str(i.rs1()), - reg_to_str(i.rs2()), - i.imm() - ) + println!("beq {:?},{:?},{}", i.rs1(), i.rs2(), i.imm()) } fn ecall(&mut self) { diff --git a/src/elf.rs b/src/elf.rs index 9a997e58..0d57747a 100644 --- a/src/elf.rs +++ b/src/elf.rs @@ -59,10 +59,10 @@ pub struct ElfMetadata { pub code_length: u64, } -pub fn load_file( - object_file: &Path, - memory_limit: usize, -) -> Option<(Vec, Vec, ElfMetadata)> { +pub fn load_file

(object_file: P, memory_limit: usize) -> Option<(Vec, Vec, ElfMetadata)> +where + P: AsRef, +{ match fs::read(object_file) { Ok(buffer) => unsafe { load(buffer.as_slice(), memory_limit) }, _ => None, @@ -118,7 +118,6 @@ pub unsafe fn load(image: &[u8], memory_limit: usize) -> Option<(Vec, Vec Option<(Vec, Vec 0_u64, }; - println!("memory.len(): {}", memory.len()); - println!("code_length: {}", code_length); - let data_segment = memory.split_off(code_length as usize); - println!("code_segment.len(): {}", memory.len()); - println!("data_segment.len(): {}", data_segment.len()); - Some(( memory, data_segment, @@ -151,12 +144,13 @@ pub unsafe fn load(image: &[u8], memory_limit: usize) -> Option<(Vec, Vec Result<(), String> { - let (graph, data_segment, elf_metadata) = cfg::build_from_file(input)?; + let (graph, data_segment, elf_metadata) = build_cfg_from_file(input)?; let potential_assignment = create_candidate_paths(&graph) .into_iter() diff --git a/src/formula_graph.rs b/src/formula_graph.rs index c3a0ab9d..112c7307 100644 --- a/src/formula_graph.rs +++ b/src/formula_graph.rs @@ -5,15 +5,15 @@ use core::fmt; pub use petgraph::graph::NodeIndex; use petgraph::Graph; use riscv_decode::types::{BType, IType, RType, SType, UType}; -use riscv_decode::Instruction; +use riscv_decode::{Instruction, Register}; pub type Formula = Graph; -static REG_SP: usize = 2; -static REG_A0: usize = 10; -static REG_A1: usize = 11; -static REG_A2: usize = 12; -static REG_A7: usize = 17; +pub static REG_SP: usize = 2; +pub static REG_A0: usize = 10; +pub static REG_A1: usize = 11; +pub static REG_A2: usize = 12; +pub static REG_A7: usize = 17; #[allow(dead_code)] pub enum SyscallId { @@ -24,7 +24,7 @@ pub enum SyscallId { Brk = 214, } -fn instruction_to_str(i: Instruction) -> &'static str { +pub fn instruction_to_str(i: Instruction) -> &'static str { match i { Instruction::Lui(_) => "lui", Instruction::Jal(_) => "jal", @@ -39,7 +39,7 @@ fn instruction_to_str(i: Instruction) -> &'static str { Instruction::Mul(_) => "mul", Instruction::Divu(_) => "divu", Instruction::Remu(_) => "remu", - Instruction::Ecall => "ecall", + Instruction::Ecall(_) => "ecall", _ => "unknown", } } @@ -244,11 +244,11 @@ impl<'a> DataFlowGraphBuilder<'a> { } fn execute_lui(&mut self, utype: UType) -> Option { - if utype.rd() == 0 { + if utype.rd() == Register::Zero { return None; } - let immediate = u64::from(utype.imm()); //sign_extend_utype(utype.imm()); + let immediate = u64::from(utype.imm()); let result = Value::Concrete(immediate); @@ -273,20 +273,24 @@ impl<'a> DataFlowGraphBuilder<'a> { where Op: FnOnce(u64, u64) -> u64, { - if itype.rd() == 0 { + if itype.rd() == Register::Zero { return None; } let rs1_value = self.regs[itype.rs1() as usize]; - let immediate = sign_extend_itype_stype(itype.imm()); - let result = self.execute_binary_op(instruction, rs1_value, Value::Concrete(immediate), op); + let result = self.execute_binary_op( + instruction, + rs1_value, + Value::Concrete(itype.imm() as u64), + op, + ); println!( "{} rs1: {:?} imm: {:?} -> rd: {:?}", instruction_to_str(instruction), rs1_value, - immediate as i64, + itype.imm() as i64, result, ); @@ -304,7 +308,7 @@ impl<'a> DataFlowGraphBuilder<'a> { where Op: FnOnce(u64, u64) -> u64, { - if rtype.rd() == 0 { + if rtype.rd() == Register::Zero { return None; } @@ -509,9 +513,9 @@ impl<'a> DataFlowGraphBuilder<'a> { } fn execute_load(&mut self, instruction: Instruction, itype: IType) -> Option { - if itype.rd() != 0 { + if itype.rd() != Register::Zero { if let Value::Concrete(base_address) = self.regs[itype.rs1() as usize] { - let immediate = sign_extend_itype_stype(itype.imm()); + let immediate = itype.imm() as u64; let address = base_address.wrapping_add(immediate); @@ -536,9 +540,9 @@ impl<'a> DataFlowGraphBuilder<'a> { fn execute_store(&mut self, instruction: Instruction, stype: SType) -> Option { if let Value::Concrete(base_address) = self.regs[stype.rs1() as usize] { - let immediate = sign_extend_itype_stype(stype.imm()); + let immediate = stype.imm(); - let address = base_address.wrapping_add(immediate); + let address = base_address.wrapping_add(immediate as u64); let value = self.regs[stype.rs2() as usize]; @@ -560,7 +564,7 @@ impl<'a> DataFlowGraphBuilder<'a> { fn execute(&mut self, instruction: Instruction) -> Option { match instruction { - Instruction::Ecall => self.execute_ecall(), + Instruction::Ecall(_) => self.execute_ecall(), Instruction::Lui(utype) => self.execute_lui(utype), Instruction::Addi(itype) => self.execute_itype(instruction, itype, u64::wrapping_add), Instruction::Add(rtype) => self.execute_rtype(instruction, rtype, u64::wrapping_add), @@ -575,13 +579,13 @@ impl<'a> DataFlowGraphBuilder<'a> { Instruction::Ld(itype) => self.execute_load(instruction, itype), Instruction::Sd(stype) => self.execute_store(instruction, stype), Instruction::Jal(jtype) => { - if jtype.rd() != 0 { + if jtype.rd() != Register::Zero { self.regs[jtype.rd() as usize] = Value::Concrete(0); } None } Instruction::Jalr(itype) => { - if itype.rd() != 0 { + if itype.rd() != Register::Zero { self.regs[itype.rd() as usize] = Value::Concrete(0); } None @@ -592,9 +596,11 @@ impl<'a> DataFlowGraphBuilder<'a> { } } -pub fn sign_extend(n: u64, b: u32) -> u64 { +pub fn sign_extend(n: u32, b: u32) -> u64 { // assert: 0 <= n <= 2^b // assert: 0 < b < CPUBITWIDTH + let n = u64::from(n); + if n < 2_u64.pow(b - 1) { n } else { @@ -603,12 +609,20 @@ pub fn sign_extend(n: u64, b: u32) -> u64 { } #[allow(dead_code)] -fn sign_extend_utype(imm: u32) -> u64 { - sign_extend(u64::from(imm), 20) +pub fn sign_extend_utype(imm: u32) -> u64 { + sign_extend(imm, 20) +} + +pub fn sign_extend_jtype(imm: u32) -> u64 { + sign_extend(imm, 21) +} + +pub fn sign_extend_itype_stype(imm: u32) -> u64 { + sign_extend(imm, 12) } -fn sign_extend_itype_stype(imm: u32) -> u64 { - sign_extend(u64::from(imm), 12) +pub fn sign_extend_btype(imm: u32) -> u64 { + sign_extend(imm, 13) } #[allow(dead_code)] @@ -633,7 +647,7 @@ pub fn build_dataflow_graph( mod tests { use super::*; use crate::candidate_path::create_candidate_paths; - use crate::cfg; + use crate::cfg::build_cfg_from_file; use petgraph::dot::Dot; use serial_test::serial; use std::env::current_dir; @@ -663,7 +677,7 @@ mod tests { let test_file = Path::new("symbolic/simple-if-else-symbolic-exit.riscu.o"); - let (graph, data_segment, elf_metadata) = cfg::build_from_file(test_file).unwrap(); + let (graph, data_segment, elf_metadata) = build_cfg_from_file(test_file).unwrap(); println!("{:?}", data_segment); diff --git a/src/main.rs b/src/main.rs index 3a3c64e1..b2d7fe9e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,11 @@ use std::path::Path; mod cli; -use monster::{cfg, disassemble::disassemble_riscu, engine}; +use monster::{ + cfg::{build_cfg_from_file, write_to_file}, + disassemble::disassemble_riscu, + engine, +}; fn main() { let matches = cli::args().get_matches(); @@ -32,19 +36,9 @@ fn main() { let input = Path::new(cfg_args.value_of("input-file").unwrap()); let output = Path::new(cfg_args.value_of("output-file").unwrap()); - let (graph, _, _) = cfg::build_from_file(Path::new(input))?; + let (graph, _, _) = build_cfg_from_file(Path::new(input))?; - if let Some(_format @ "png") = cfg_args.value_of("format") { - let tmp = Path::new(".tmp-cfg.dot"); - - cfg::write_to_file(&graph, tmp).map_err(|e| e.to_string())?; - - cfg::convert_dot_to_png(tmp, output)?; - - std::fs::remove_file(tmp).map_err(|e| e.to_string())?; - } else { - cfg::write_to_file(&graph, output).map_err(|e| e.to_string())?; - } + write_to_file(&graph, output).map_err(|e| e.to_string())?; Ok(()) }); diff --git a/tests/cfg.rs b/tests/cfg.rs new file mode 100644 index 00000000..80597a34 --- /dev/null +++ b/tests/cfg.rs @@ -0,0 +1,32 @@ +use monster::cfg::*; +use petgraph::dot::Dot; +use std::{fs::File, io::prelude::*}; + +mod common; + +use common::time; + +#[test] +fn can_build_control_flow_graph() { + common::forall_compiled_riscu(move |path| { + let (graph, _, _) = time(format!("compute cfg: {:?}", path), || { + build_cfg_from_file(path.clone()).unwrap() + }); + + let dot_graph = Dot::with_config(&graph, &[]); + + let dot_file = path.clone().with_extension("dot"); + + let mut f = File::create(dot_file.clone()).unwrap(); + f.write_fmt(format_args!("{:?}", dot_graph)).unwrap(); + + //let png_file = dot_file.with_extension("png"); + + //let dfile = dot_file.as_path(); + //let pfile = png_file.as_path(); + + //time(String::from("dot-to-png"), || { + // common::convert_dot_to_png(dfile, pfile).unwrap(); + //}); + }); +} diff --git a/tests/common.rs b/tests/common.rs index 6d6da15c..c494f20e 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -1,12 +1,19 @@ +#![allow(dead_code)] + use std::{ env::current_dir, + fs::read_dir, path::{Path, PathBuf}, process::Command, + time::Instant, }; -pub fn compile(source_file: &Path) -> Result { - let directory = source_file.parent().unwrap(); - let target = source_file.with_extension("o"); +pub fn compile

(source_file: P) -> Result +where + P: AsRef, +{ + let directory = source_file.as_ref().parent().unwrap(); + let target = source_file.as_ref().with_extension("o"); Command::new("docker") .arg("run") @@ -18,7 +25,10 @@ pub fn compile(source_file: &Path) -> Result { .arg("cksystemsteaching/selfie") .arg("/opt/selfie/selfie") .arg("-c") - .arg(format!("/opt/monster/{}", source_file.to_str().unwrap())) + .arg(format!( + "/opt/monster/{}", + source_file.as_ref().to_str().unwrap() + )) .arg("-o") .arg(format!("/opt/monster/{}", target.to_str().unwrap())) .current_dir(directory) @@ -27,3 +37,71 @@ pub fn compile(source_file: &Path) -> Result { Ok(target) } + +/// Convert a dot file into a png file (depends on graphviz) +pub fn convert_dot_to_png

(source: P, output: P) -> Result<(), &'static str> +where + P: AsRef, +{ + Command::new("dot") + .arg("-Tpng") + .arg(source.as_ref().to_path_buf()) + .arg("-o") + .arg(output.as_ref().to_path_buf()) + .output() + .map_err(|_| "Cannot convert CFG to png file (is graphviz installed?)")?; + + Ok(()) +} + +pub fn time(s: String, mut f: F) -> R +where + F: FnMut() -> R, +{ + let start = Instant::now(); + let result = f(); + let end = Instant::now(); + + println!("{}: {:?}", s, end.duration_since(start)); + + result +} + +pub fn forall_compiled_riscu(f: F) +where + F: Fn(PathBuf) + Send + Sync + 'static, +{ + let compiled = read_dir("symbolic") + .unwrap() + .map(|dir_entry| dir_entry.unwrap().path()) + .filter(|path| { + if let Some(extension) = path.extension() { + extension == "c" + } else { + false + } + }) + .map(|input_source| { + time( + format!("compile: {}", input_source.to_str().unwrap()), + || compile(input_source.clone()).unwrap(), + ) + }); + + //let f = std::sync::Arc::new(f); + + compiled.for_each(|binary_file_name| { + //let f = f.clone(); + //thread::spawn(move || { + time( + format!("compute CFG: {}", binary_file_name.to_str().unwrap()), + || { + f(binary_file_name.clone()); + }, + ); + //}) + }); + //.for_each(|t| { + //t.join().unwrap(); + //}) +}