Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: New core API #341

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
core: completely remove graph_algos. Add to_dot impls in disassembler…
… and in function using petgraph. remove unnecessary imports field from Project
  • Loading branch information
m4b committed Oct 19, 2017
commit 637441b6b702fe170f9dac9be4ede97ac4d45975
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -171,7 +171,7 @@ fn print_reverse_deps<Function: Fun, W: Write + WriteColor>(mut fmt: W, program:
fn disassemble<Function: Fun + DataFlow + Send>(binary: &str) -> Result<Program<Function>> {
let (mut proj, machine) = loader::load(Path::new(&binary))?;
let program = proj.code.pop().unwrap();
let reg = proj.region().clone();
let reg = proj.region();
info!("disassembly thread started");
Ok(match machine {
Machine::Avr => analyze::<avr::Avr, Function>(program, reg.clone(), avr::Mcu::atmega103()),
1 change: 0 additions & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -14,7 +14,6 @@ uuid = { version = "0.5", features = ["v4", "serde"]}
flate2 = "0.2.13"
byteorder = "1"
goblin = "0.0.11"
panopticon-graph-algos = { path = "../graph-algos" }
serde = { version = "1.0", features = ["rc"] }
serde_derive = "1.0"
serde_cbor = "0.6"
90 changes: 31 additions & 59 deletions core/src/disassembler.rs
Original file line number Diff line number Diff line change
@@ -89,8 +89,7 @@
use {Guard, Mnemonic, Region, Result, Rvalue, Statement};

use num::traits::{NumCast, One, Zero};
use panopticon_graph_algos::{AdjacencyList, EdgeListGraphTrait, GraphTrait, IncidenceGraphTrait, MutableGraphTrait, VertexListGraphTrait};
use panopticon_graph_algos::adjacency_list::AdjacencyListVertexDescriptor;
use petgraph::prelude::*;
use std::collections::HashMap;
use std::fmt;
use std::fmt::Debug;
@@ -263,7 +262,7 @@ impl<A: Architecture> State<A> {
}

/// Single matching rule.
#[derive(Clone)]
#[derive(Debug,Clone)]
pub enum Rule<A: Architecture> {
/// Matches a fixed set of bits of a single token
Terminal {
@@ -289,58 +288,33 @@ impl<A: Architecture> PartialEq for Rule<A> {
}
}

type DisassemblerRef = NodeIndex<u32>;

/// Ready made disassembler for simple instruction sets.
///
/// Disassembler instances are creates using the `new_disassembler!` macro. The resulting
/// disassembler can then be used to produce `Match`es.
pub struct Disassembler<A: Architecture> {
graph: AdjacencyList<(), Rule<A>>,
start: AdjacencyListVertexDescriptor,
end: HashMap<AdjacencyListVertexDescriptor, Arc<Action<A>>>,
graph: Graph<(), Rule<A>>,
start: DisassemblerRef,
end: HashMap<DisassemblerRef, Arc<Action<A>>>,
default: Option<Action<A>>,
}

impl<A: Architecture> Disassembler<A> {
impl<A: Architecture + Debug> Disassembler<A> {
/// Creates a new, empty, disassembler instance. You probably want to use `new_disassembler!`
/// instead.
pub fn new() -> Disassembler<A> {
let mut g = AdjacencyList::new();
let s = g.add_vertex(());
let mut g = Graph::new();
let s = g.add_node(());

Disassembler { graph: g, start: s, end: HashMap::new(), default: None }
}
/// Converts to a dot file; useful for debugging
pub fn to_dot(&self) {
println!("digraph G {{");
for v in self.graph.vertices() {
let lb = self.graph.vertex_label(v).unwrap();

if self.end.contains_key(&v) {
println!(
"{} [label=\"{}, prio: {:?}\",shape=doublecircle]",
v.0,
v.0,
lb
);
} else {
println!("{} [label=\"{}, prio: {:?}\",shape=circle]", v.0, v.0, lb);
}
}
for e in self.graph.edges() {
let lb = match self.graph.edge_label(e) {
Some(&Rule::Sub(_)) => "SUB".to_string(),
Some(&Rule::Terminal::<A> { ref pattern, ref mask, .. }) => format!("{:?}/{:?}", pattern, mask),
None => "".to_string(),
};
println!(
"{} -> {} [label={:?}]",
self.graph.source(e).0,
self.graph.target(e).0,
lb
);
}
println!("}}");
}
pub fn to_dot(&self) {
use petgraph::dot::Dot;
println!("{:?}", Dot::new(&self.graph));
}

/// Adds the matching rule and associated semantic action.
/// Panics if a is empty.
@@ -351,19 +325,18 @@ impl<A: Architecture> Disassembler<A> {
for r in a.iter() {
let mut found = false;

for out in self.graph.out_edges(v) {
if let Some(ref t) = self.graph.edge_label(out) {
if **t == *r {
v = self.graph.target(out);
found = true;
break;
}
for out in self.graph.edges_directed(v, Direction::Outgoing) {
let t = out.weight();
if t == r {
v = out.target();
found = true;
break;
}
}

if !found {
let tmp = self.graph.add_vertex(());
self.graph.add_edge(r.clone(), v, tmp);
let tmp = self.graph.add_node(());
self.graph.add_edge(v, tmp, r.clone());
v = tmp;
}
}
@@ -445,7 +418,7 @@ impl<A: Architecture> Disassembler<A> {
Iter: Iterator<Item = Option<u8>> + Clone,
A::Configuration: Clone,
{
let mut states = Vec::<(Vec<&()>, State<A>, AdjacencyListVertexDescriptor, Iter)>::new();
let mut states = Vec::<(Vec<&()>, State<A>, DisassemblerRef, Iter)>::new();
let mut ret = vec![];

states.push((vec![], initial_state.clone(), self.start, i.clone()));
@@ -459,14 +432,14 @@ impl<A: Architecture> Disassembler<A> {
}
}

let mut new_states = Vec::<(Vec<&()>, State<A>, AdjacencyListVertexDescriptor, Iter)>::new();
let mut new_states = Vec::<(Vec<&()>, State<A>, DisassemblerRef, Iter)>::new();


for &(ref pats, ref state, ref vx, ref iter) in states.iter() {
if let Some(a) = self.graph.vertex_label(*vx) {
for e in self.graph.out_edges(*vx) {
match self.graph.edge_label(e) {
Some(&Rule::Terminal { ref mask, ref pattern, capture_group: ref capture }) => {
if let Some(a) = self.graph.node_weight(*vx) {
for e in self.graph.edges_directed(*vx, Direction::Outgoing) {
match e.weight() {
&Rule::Terminal { ref mask, ref pattern, capture_group: ref capture } => {
let mut i = iter.clone();
if let Some(tok) = Self::read_token(&mut i) {
if mask.clone() & tok.clone() == *pattern {
@@ -509,17 +482,16 @@ impl<A: Architecture> Disassembler<A> {

p.push(a);
st.tokens.push(tok);
new_states.push((p, st, self.graph.target(e), i));
new_states.push((p, st, e.target(), i));
}
}
}
Some(&Rule::Sub(ref sub)) => {
&Rule::Sub(ref sub) => {
let i = iter.clone();
let mut v = sub.find(i.clone(), state);

new_states.extend(v.drain(..).map(|(a, b, i)| (a, b, self.graph.target(e), i.clone())));
new_states.extend(v.drain(..).map(|(a, b, i)| (a, b, e.target(), i.clone())));
}
None => {}
};
}
}
56 changes: 6 additions & 50 deletions core/src/function.rs
Original file line number Diff line number Diff line change
@@ -722,57 +722,13 @@ impl Function {
pub fn statements<'b>(&'b self) -> Box<Iterator<Item=&'b Statement> + 'b> {
Box::new(self.basic_blocks().map(|bb| bb.statements()).flat_map(|ss| ss))
}
}
///// Returns the functions basic block graph in graphivz's DOT format. Useful for debugging.
// pub fn to_dot(&self) -> String {
// let mut ret = "digraph G {".to_string();
//
// for v in self.cflow_graph.node_indices() {
// match self.cflow_graph.node_weight(v) {
// Some(&ControlFlowTarget::Resolved(ref bb)) => {
// ret = format!(
// "{}\n{} [label=<<table border=\"0\"><tr><td>{}:{}</td></tr>",
// ret,
// v.0,
// bb.area.start,
// bb.area.end
// );
//
// for mne in bb.mnemonics.iter() {
// ret = format!("{}<tr><td align=\"left\">{}</td></tr>", ret, mne.opcode);
// for i in mne.instructions.iter() {
// ret = format!(
// "{}<tr><td align=\"left\">&nbsp;&nbsp;&nbsp;&nbsp;{}</td></tr>",
// ret,
// i
// );
// }
// }
//
// ret = format!("{}</table>>,shape=record];", ret);
// }
// Some(&ControlFlowTarget::Unresolved(ref c)) => {
// ret = format!("{}\n{} [label=\"{:?}\",shape=circle];", ret, v.0, c);
// }
// _ => {
// ret = format!("{}\n{} [label=\"?\",shape=circle];", ret, v.0);
// }
// }
// }
//
//// for e in self.cflow_graph.edges() {
//// ret = format!(
//// "{}\n{} -> {} [label=\"{}\"];",
//// ret,
//// self.cflow_graph.source(e).0,
//// self.cflow_graph.target(e).0,
//// self.cflow_graph.edge_label(e).unwrap()
//// );
//// }
//
// format!("{}\n}}", ret)
// }

/// Returns the functions basic block graph in graphivz's DOT format. Useful for debugging.
pub fn to_dot(&self) -> String {
use petgraph::dot::Dot;
format!("{:?}", Dot::new(&self.cflow_graph))
}
}

#[cfg(test)]
mod tests {
1 change: 0 additions & 1 deletion core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -80,7 +80,6 @@ extern crate log;

extern crate num;
extern crate flate2;
extern crate panopticon_graph_algos;
extern crate uuid;
extern crate byteorder;
extern crate goblin;
18 changes: 8 additions & 10 deletions core/src/loader.rs
Original file line number Diff line number Diff line change
@@ -128,11 +128,10 @@ pub fn load_mach<F: Fun>(bytes: &[u8], offset: usize, name: String) -> Result<(P

for import in binary.imports()? {
debug!("Import {}: {:#x}", import.name, import.offset);
proj.imports.insert(import.offset, import.name.to_string());
prog.imports.insert(import.offset, import.name.to_string());
}

debug!("Imports: {:?}", &proj.imports);
prog.imports = proj.imports.clone();
debug!("Imports: {:?}", &prog.imports);
proj.comments.insert(("base".to_string(), entry), "main".to_string());
proj.code.push(prog);

@@ -213,13 +212,13 @@ fn load_elf<F: Fun>(bytes: &[u8], name: String) -> Result<(Project<F>, Machine)>
}
};

let resolve_import_address = |proj: &mut Project<F>, relocs: &[elf::Reloc], name: &str| {
let resolve_import_address = |prog: &mut Program<F>, relocs: &[elf::Reloc], name: &str| {
for reloc in relocs {
let pltsym = &binary.dynsyms[reloc.r_sym];
let pltname = &binary.dynstrtab[pltsym.st_name];
if pltname == name {
debug!("Import match {}: {:#x} {:?}", name, reloc.r_offset, pltsym);
proj.imports.insert(reloc.r_offset as u64, name.to_string());
prog.imports.insert(reloc.r_offset as u64, name.to_string());
return true;
}
}
@@ -236,15 +235,15 @@ fn load_elf<F: Fun>(bytes: &[u8], name: String) -> Result<(Project<F>, Machine)>
seen_syms.insert(sym.st_value);

let name = &binary.dynstrtab[sym.st_name];
if !resolve_import_address(&mut proj, &binary.pltrelocs, name) {
if !resolve_import_address(&mut prog, &binary.pltrelocs, name) {
if sym.is_function() {
if !resolve_import_address(&mut proj, &binary.dynrelas, name) {
resolve_import_address(&mut proj, &binary.dynrels, name);
if !resolve_import_address(&mut prog, &binary.dynrelas, name) {
resolve_import_address(&mut prog, &binary.dynrels, name);
}
}
}
}
debug!("Imports: {:#?}", &proj.imports);
debug!("Imports: {:#?}", &prog.imports);

// add strippable symbol information
for sym in &binary.syms {
@@ -254,7 +253,6 @@ fn load_elf<F: Fun>(bytes: &[u8], name: String) -> Result<(Project<F>, Machine)>
}
seen_syms.insert(sym.st_value);
}
prog.imports = proj.imports.clone();
proj.comments.insert(("base".to_string(), entry), "main".to_string());
proj.code.push(prog);

6 changes: 1 addition & 5 deletions core/src/project.rs
Original file line number Diff line number Diff line change
@@ -22,7 +22,6 @@


use {CallGraphRef, Fun, Program, Region, Result, World};
use panopticon_graph_algos::GraphTrait;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use flate2::Compression;
use flate2::read::ZlibDecoder;
@@ -48,8 +47,6 @@ pub struct Project<F> {
pub data: World,
/// Comments
pub comments: HashMap<(String, u64), String>,
/// Symbolic References (Imports)
pub imports: HashMap<u64, String>,
}

impl<'de, F: Fun + Deserialize<'de> + Serialize> Project<F> {
@@ -106,14 +103,13 @@ impl<F> Project<F> {
code: Vec::new(),
data: World::new(r),
comments: HashMap::new(),
imports: HashMap::new(),
}
}

/// Returns this project's root Region
pub fn region(&self) -> &Region {
// this cannot fail because World::new guarantees that data.root = r
self.data.dependencies.vertex_label(self.data.root).unwrap()
self.data.dependencies.node_weight(self.data.root).unwrap()
}
}

Loading