Unverified Commit 542db66e by Melvin Klimke Committed by GitHub

Direction independent graph (#118)

Adds CallSource nodes to the control flow graph, which have the same role for backwards analysis as the CallReturn nodes have for forward analysis.
parent 434d0727
...@@ -32,11 +32,12 @@ ...@@ -32,11 +32,12 @@
//! (if the call returns at all). //! (if the call returns at all).
//! * Right now indirect calls are handled as if they were extern calls, i.e. an *ExternCallStub* edge is added. //! * Right now indirect calls are handled as if they were extern calls, i.e. an *ExternCallStub* edge is added.
//! This behaviour will change in the future, when better indirect call handling is implemented. //! This behaviour will change in the future, when better indirect call handling is implemented.
//! * For each in-program call ([`image`](../../../../../doc/images/internal_function_call.png)) and corresponding return jump one node and three edges are generated: //! * For each in-program call ([`image`](../../../../../doc/images/internal_function_call.png)) and corresponding return jump two nodes and four edges are generated:
//! * An artificial node *CallReturn* //! * An artificial node *CallReturn* and node *CallSource*
//! * A *CRCallStub* edge from the *BlkEnd* node of the callsite to *CallReturn* //! * A *CRCallStub* edge from the *BlkEnd* node of the callsite to *CallReturn*
//! * A *CRReturnStub* edge from the *BlkEnd* node of the returning from block to *CallReturn* //! * A *CRReturnStub* edge from the *BlkEnd* node of the returning from block to *CallReturn*
//! * A *CRCombine* edge from *CallReturn* to the *BlkStart* node of the returned to block. //! * A *ReturnCombine* edge from *CallReturn* to the *BlkStart* node of the returned to block.
//! * A *CallCombine* edge from the *BlkEnd* node to the *CallSource* node.
//! //!
//! The artificial *CallReturn* nodes enable enriching the information flowing through a return edge //! The artificial *CallReturn* nodes enable enriching the information flowing through a return edge
//! with information recovered from the corresponding callsite during a fixpoint computation. //! with information recovered from the corresponding callsite during a fixpoint computation.
...@@ -52,9 +53,12 @@ pub type Graph<'a> = DiGraph<Node<'a>, Edge<'a>>; ...@@ -52,9 +53,12 @@ pub type Graph<'a> = DiGraph<Node<'a>, Edge<'a>>;
/// The node type of an interprocedural control flow graph /// The node type of an interprocedural control flow graph
/// ///
/// Each node carries a pointer to its associated block with it. /// Each node carries a pointer to its associated block with it.
/// For `CallReturn`nodes the associated blocks are both the callsite block (containing the call instruction) /// For `CallReturn`nodes the associated blocks are both the `CallSource`block (containing the call instruction)
/// and the returning-from block (containing the return instruction). /// and the returning-from block (containing the return instruction).
/// ///
/// For `CallSource`nodes the associated block is the callsite block (source)
/// and the target block of the call.
///
/// Basic blocks are allowed to be contained in more than one `Sub`. /// Basic blocks are allowed to be contained in more than one `Sub`.
/// In the control flow graph such basic blocks occur once per subroutine they are contained in. /// In the control flow graph such basic blocks occur once per subroutine they are contained in.
/// For this reason, the nodes also carry a pointer to the corresponding subroutine with them /// For this reason, the nodes also carry a pointer to the corresponding subroutine with them
...@@ -67,6 +71,10 @@ pub enum Node<'a> { ...@@ -67,6 +71,10 @@ pub enum Node<'a> {
call: (&'a Term<Blk>, &'a Term<Sub>), call: (&'a Term<Blk>, &'a Term<Sub>),
return_: (&'a Term<Blk>, &'a Term<Sub>), return_: (&'a Term<Blk>, &'a Term<Sub>),
}, },
CallSource {
source: (&'a Term<Blk>, &'a Term<Sub>),
target: (&'a Term<Blk>, &'a Term<Sub>),
},
} }
impl<'a> Node<'a> { impl<'a> Node<'a> {
...@@ -76,7 +84,9 @@ impl<'a> Node<'a> { ...@@ -76,7 +84,9 @@ impl<'a> Node<'a> {
use Node::*; use Node::*;
match self { match self {
BlkStart(blk, _sub) | BlkEnd(blk, _sub) => blk, BlkStart(blk, _sub) | BlkEnd(blk, _sub) => blk,
CallReturn { .. } => panic!("get_block() is undefined for CallReturn nodes"), CallSource { .. } | CallReturn { .. } => {
panic!("get_block() is undefined for CallReturn and CallSource nodes")
}
} }
} }
} }
...@@ -95,6 +105,11 @@ impl<'a> std::fmt::Display for Node<'a> { ...@@ -95,6 +105,11 @@ impl<'a> std::fmt::Display for Node<'a> {
"CallReturn @ {} (sub {}) (caller @ {} (sub {}))", "CallReturn @ {} (sub {}) (caller @ {} (sub {}))",
return_.0.tid, return_.1.tid, call.0.tid, call.1.tid return_.0.tid, return_.1.tid, call.0.tid, call.1.tid
), ),
Self::CallSource { source, target } => write!(
formatter,
"CallSource @ {} (sub {}) (caller @ {} (sub {}))",
target.0.tid, target.1.tid, source.0.tid, source.1.tid
),
} }
} }
} }
...@@ -115,7 +130,8 @@ pub enum Edge<'a> { ...@@ -115,7 +130,8 @@ pub enum Edge<'a> {
ExternCallStub(&'a Term<Jmp>), ExternCallStub(&'a Term<Jmp>),
CRCallStub, CRCallStub,
CRReturnStub, CRReturnStub,
CRCombine(&'a Term<Jmp>), CallCombine(&'a Term<Jmp>),
ReturnCombine(&'a Term<Jmp>),
} }
/// A builder struct for building graphs /// A builder struct for building graphs
...@@ -176,7 +192,7 @@ impl<'a> GraphBuilder<'a> { ...@@ -176,7 +192,7 @@ impl<'a> GraphBuilder<'a> {
} }
/// add all subs to the call targets so that call instructions can be linked to the starting block of the corresponding sub. /// add all subs to the call targets so that call instructions can be linked to the starting block of the corresponding sub.
fn add_subs_to_jump_targets(&mut self) { fn add_subs_to_call_targets(&mut self) {
for sub in self.program.term.subs.iter() { for sub in self.program.term.subs.iter() {
if !sub.term.blocks.is_empty() { if !sub.term.blocks.is_empty() {
let start_block = &sub.term.blocks[0]; let start_block = &sub.term.blocks[0];
...@@ -194,8 +210,8 @@ impl<'a> GraphBuilder<'a> { ...@@ -194,8 +210,8 @@ impl<'a> GraphBuilder<'a> {
jump: &'a Term<Jmp>, jump: &'a Term<Jmp>,
untaken_conditional: Option<&'a Term<Jmp>>, untaken_conditional: Option<&'a Term<Jmp>>,
) { ) {
let sub_term = match self.graph[source] { let (source_block, sub_term) = match self.graph[source] {
Node::BlkEnd(_source_block, sub_term) => sub_term, Node::BlkEnd(source_block, sub_term) => (source_block, sub_term),
_ => panic!(), _ => panic!(),
}; };
match &jump.term { match &jump.term {
...@@ -242,14 +258,34 @@ impl<'a> GraphBuilder<'a> { ...@@ -242,14 +258,34 @@ impl<'a> GraphBuilder<'a> {
.add_edge(source, return_to_node, Edge::ExternCallStub(jump)); .add_edge(source, return_to_node, Edge::ExternCallStub(jump));
} }
} else { } else {
let mut call_source_node: Option<NodeIndex> = None;
if let Some((target_node, _)) = self.call_targets.get(&target) { if let Some((target_node, _)) = self.call_targets.get(&target) {
self.graph.add_edge(source, *target_node, Edge::Call(jump)); let (target_block, target_sub) = match self.graph[*target_node] {
Node::BlkStart(target_block, target_sub) => (target_block, target_sub),
_ => panic!(),
};
call_source_node = Some(self.graph.add_node(Node::CallSource {
source: (source_block, sub_term),
target: (target_block, target_sub),
}));
self.graph.add_edge(
source,
*call_source_node.as_ref().unwrap(),
Edge::CallCombine(jump),
);
self.graph.add_edge(
*call_source_node.as_ref().unwrap(),
*target_node,
Edge::Call(jump),
);
} // TODO: Log message for the else-case? } // TODO: Log message for the else-case?
if let Some(return_node) = return_to_node_option { if let Some(return_node) = return_to_node_option {
self.return_addresses if let Some(cs_node) = call_source_node {
.entry(target.clone()) self.return_addresses
.and_modify(|vec| vec.push((source, return_node))) .entry(target.clone())
.or_insert_with(|| vec![(source, return_node)]); .and_modify(|vec| vec.push((cs_node, return_node)))
.or_insert_with(|| vec![(cs_node, return_node)]);
}
} }
} }
} }
...@@ -310,7 +346,7 @@ impl<'a> GraphBuilder<'a> { ...@@ -310,7 +346,7 @@ impl<'a> GraphBuilder<'a> {
} }
for (call_node, return_to_node) in self.return_addresses[&return_from_sub.tid].iter() { for (call_node, return_to_node) in self.return_addresses[&return_from_sub.tid].iter() {
let (call_block, caller_sub) = match self.graph[*call_node] { let (call_block, caller_sub) = match self.graph[*call_node] {
Node::BlkEnd(block, sub) => (block, sub), Node::CallSource { source, .. } => source,
_ => panic!(), _ => panic!(),
}; };
let return_from_block = self.graph[return_source].get_block(); let return_from_block = self.graph[return_source].get_block();
...@@ -320,16 +356,19 @@ impl<'a> GraphBuilder<'a> { ...@@ -320,16 +356,19 @@ impl<'a> GraphBuilder<'a> {
.iter() .iter()
.find(|jump| matches!(jump.term, Jmp::Call{..})) .find(|jump| matches!(jump.term, Jmp::Call{..}))
.unwrap(); .unwrap();
let cr_combine_node = self.graph.add_node(Node::CallReturn { let return_combine_node = self.graph.add_node(Node::CallReturn {
call: (call_block, caller_sub), call: (call_block, caller_sub),
return_: (return_from_block, return_from_sub), return_: (return_from_block, return_from_sub),
}); });
self.graph self.graph
.add_edge(*call_node, cr_combine_node, Edge::CRCallStub); .add_edge(*call_node, return_combine_node, Edge::CRCallStub);
self.graph
.add_edge(return_source, cr_combine_node, Edge::CRReturnStub);
self.graph self.graph
.add_edge(cr_combine_node, *return_to_node, Edge::CRCombine(call_term)); .add_edge(return_source, return_combine_node, Edge::CRReturnStub);
self.graph.add_edge(
return_combine_node,
*return_to_node,
Edge::ReturnCombine(call_term),
);
} }
} }
...@@ -367,7 +406,7 @@ impl<'a> GraphBuilder<'a> { ...@@ -367,7 +406,7 @@ impl<'a> GraphBuilder<'a> {
/// Build the interprocedural control flow graph. /// Build the interprocedural control flow graph.
pub fn build(mut self) -> Graph<'a> { pub fn build(mut self) -> Graph<'a> {
self.add_program_blocks(); self.add_program_blocks();
self.add_subs_to_jump_targets(); self.add_subs_to_call_targets();
self.add_jump_and_call_edges(); self.add_jump_and_call_edges();
self.add_return_edges(); self.add_return_edges();
self.graph self.graph
...@@ -471,7 +510,7 @@ mod tests { ...@@ -471,7 +510,7 @@ mod tests {
let program = mock_program(); let program = mock_program();
let graph = get_program_cfg(&program, HashSet::new()); let graph = get_program_cfg(&program, HashSet::new());
println!("{}", serde_json::to_string_pretty(&graph).unwrap()); println!("{}", serde_json::to_string_pretty(&graph).unwrap());
assert_eq!(graph.node_count(), 14); assert_eq!(graph.node_count(), 16);
assert_eq!(graph.edge_count(), 18); assert_eq!(graph.edge_count(), 20);
} }
} }
...@@ -148,7 +148,7 @@ impl<'a, T: Context<'a>> GeneralFPContext for GeneralizedContext<'a, T> { ...@@ -148,7 +148,7 @@ impl<'a, T: Context<'a>> GeneralFPContext for GeneralizedContext<'a, T> {
} }
} }
/// Edge transition function. /// Forward edge transition function.
/// Applies the transition functions from the interprocedural context object /// Applies the transition functions from the interprocedural context object
/// corresponding to the type of the provided edge. /// corresponding to the type of the provided edge.
fn update_edge( fn update_edge(
...@@ -169,6 +169,7 @@ impl<'a, T: Context<'a>> GeneralFPContext for GeneralizedContext<'a, T> { ...@@ -169,6 +169,7 @@ impl<'a, T: Context<'a>> GeneralFPContext for GeneralizedContext<'a, T> {
}); });
end_val.map(NodeValue::Value) end_val.map(NodeValue::Value)
} }
Edge::CallCombine(_) => Some(Self::NodeValue::Value(node_value.unwrap_value().clone())),
Edge::Call(call) => self Edge::Call(call) => self
.context .context
.update_call(node_value.unwrap_value(), call, &graph[end_node]) .update_call(node_value.unwrap_value(), call, &graph[end_node])
...@@ -181,7 +182,7 @@ impl<'a, T: Context<'a>> GeneralFPContext for GeneralizedContext<'a, T> { ...@@ -181,7 +182,7 @@ impl<'a, T: Context<'a>> GeneralFPContext for GeneralizedContext<'a, T> {
call: None, call: None,
return_: Some(node_value.unwrap_value().clone()), return_: Some(node_value.unwrap_value().clone()),
}), }),
Edge::CRCombine(call_term) => match node_value { Edge::ReturnCombine(call_term) => match node_value {
NodeValue::Value(_) => panic!("Unexpected interprocedural fixpoint graph state"), NodeValue::Value(_) => panic!("Unexpected interprocedural fixpoint graph state"),
NodeValue::CallReturnCombinator { call, return_ } => { NodeValue::CallReturnCombinator { call, return_ } => {
let return_from_block = match graph.node_weight(start_node) { let return_from_block = match graph.node_weight(start_node) {
......
...@@ -358,6 +358,9 @@ impl<'a> PointerInference<'a> { ...@@ -358,6 +358,9 @@ impl<'a> PointerInference<'a> {
Node::BlkStart(block, _sub) => { Node::BlkStart(block, _sub) => {
println!("{}: ERROR: Block start without successor state!", block.tid) println!("{}: ERROR: Block start without successor state!", block.tid)
} }
Node::CallSource { source, .. } => {
println!("{}: ERROR: Call source without target!", source.0.tid)
}
Node::CallReturn { call, return_ } => { Node::CallReturn { call, return_ } => {
let (call_state, return_state) = match node_value { let (call_state, return_state) = match node_value {
NodeValue::CallReturnCombinator { call, return_ } => { NodeValue::CallReturnCombinator { call, return_ } => {
......
...@@ -79,7 +79,8 @@ fn is_reachable( ...@@ -79,7 +79,8 @@ fn is_reachable(
match edge.weight() { match edge.weight() {
Edge::Block Edge::Block
| Edge::CRCallStub | Edge::CRCallStub
| Edge::CRCombine(_) | Edge::CallCombine(_)
| Edge::ReturnCombine(_)
| Edge::Jump(_, _) | Edge::Jump(_, _)
| Edge::ExternCallStub(_) => { | Edge::ExternCallStub(_) => {
if visited_nodes.get(&edge.target()).is_none() { if visited_nodes.get(&edge.target()).is_none() {
......
doc/images/internal_function_call.png

23.4 KB | W: | H:

doc/images/internal_function_call.png

35.3 KB | W: | H:

doc/images/internal_function_call.png
doc/images/internal_function_call.png
doc/images/internal_function_call.png
doc/images/internal_function_call.png
  • 2-up
  • Swipe
  • Onion skin
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment