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

provides liveness analysis and uses liveness for Sub.free_vars #1051

Merged
merged 3 commits into from
Feb 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
89 changes: 63 additions & 26 deletions lib/bap/bap.mli
Original file line number Diff line number Diff line change
Expand Up @@ -1959,7 +1959,7 @@ module Std : sig
(** Bil is an instance of Domain.

A flat domain with the empty Bil program being the empty element.
*)
*)
val domain : stmt list Knowledge.domain

(** Instance of the persistence class *)
Expand Down Expand Up @@ -6574,9 +6574,19 @@ module Std : sig
This is a graph where all information is distilled to term
identifiers and relations between them, that are also labeled with
term identifiers. *)
module Tid : Graph with type node = tid
and type Node.label = tid
and type Edge.label = tid
module Tid : sig
type node = tid

(** [start] is a pseudo node that is used as the entry node of a graph. *)
val start : node

(** [exit] is a pseudo node that is used as the exit node of a graph. *)
val exit : node
include Graph with type node := tid
and type Node.label = tid
and type Edge.label = tid
end

end

(** Disassembled program.
Expand Down Expand Up @@ -7294,12 +7304,24 @@ module Std : sig
dominators tree is used. *)
val free_vars : t -> Var.Set.t

(** [to_graph sub] builds a graph of subroutine [sub]. Graph nodes
are block term identifiers, and edges are labeled with term
identifiers of the jmp terms, that corresponds to the edge.
This representation is useful, if you need to compute some
graph relation on a subroutine, that will be later used to
perform its incremental transformation. *)
(** [to_graph sub] builds a graph of subroutine [sub].

Graph nodes are block term identifiers and edges are labeled
with term identifiers of the jmp terms that correspond to
the given edge.

@since 2.1 the returned graph contains two pseudo-nodes
[Graphs.Tid.start] and [Graphs.Tid.exit] so that all nodes
that has in-degree [0] or that start a strongly connected
component are connected to the [start] node (the same for
[exit] but on the reversed graph.

Edges from [start] to other nodes are labeled with the
[Graphs.Tid.start] tid.

Edges from nodes to the [exit] node are labeled with the
[Graphs.Tid.exit] tid.
*)
val to_graph : t -> Graphs.Tid.t

(** [to_cfg sub] builds a graph representation of a subroutine
Expand All @@ -7312,6 +7334,21 @@ module Std : sig
operation is just a projection, i.e., it has O(0) complexity. *)
val of_cfg : Graphs.Ir.t -> t


(** [compute_liveness sub] computes a set of live variables for each block.

For a block [b] and solution [s = compute_liveness sub],
[Solution.get s (Term.tid b)] is a set of variables that are
live at the _exit_ from this block.

A set of variables that are live (free) in the
whole subroutine is the set of variables that are live at the
[Graphs.Tid.start] node.

@since 2.1
*)
val compute_liveness : t -> (tid, Var.Set.t) Solution.t

(** other names for the given subroutine.*)
val aliases : string list tag

Expand Down Expand Up @@ -7675,25 +7712,25 @@ module Std : sig

(** [reify ()] reifies inputs into a jump term.

Calls and interrupt subroutines invocations are represented
with two edges: the normal edge (denoted [dst]) is the
intra-procedural edge which connects the callsite with the
fall-through destination (if such exists) and an alternative
destination (denoted with [alt]) which represents an
inter-procedural destination between the callsite and the
call destination.
Calls and interrupt subroutines invocations are represented
with two edges: the normal edge (denoted [dst]) is the
intra-procedural edge which connects the callsite with the
fall-through destination (if such exists) and an alternative
destination (denoted with [alt]) which represents an
inter-procedural destination between the callsite and the
call destination.

@param cnd is a core theory term that denotes the
guard condition of a conditional jump.
@param cnd is a core theory term that denotes the
guard condition of a conditional jump.

@param alt is the alternative control flow destination.
@param alt is the alternative control flow destination.

@param dst is the direct control flow destination
@param dst is the direct control flow destination

@tid is the jump identifier, if not specified a fresh
new identifier is created.
@tid is the jump identifier, if not specified a fresh
new identifier is created.

*)
*)
val reify : ?tid:tid ->
?cnd:Theory.Bool.t Theory.value ->
?alt:dst -> ?dst:dst -> unit -> t
Expand Down Expand Up @@ -8233,7 +8270,7 @@ module Std : sig

(** A factory of rooters. Useful to register custom rooters *)
module Factory : Source.Factory.S with type t = t
[@@deprecated "[since 2019-05] use [provide]"]
[@@deprecated "[since 2019-05] use [provide]"]

end

Expand Down Expand Up @@ -8264,7 +8301,7 @@ module Std : sig
val provide : t -> unit

module Factory : Source.Factory.S with type t = t
[@@deprecated "[since 2019-05] use [provide]"]
[@@deprecated "[since 2019-05] use [provide]"]

end

Expand Down
1 change: 1 addition & 0 deletions lib/bap_sema/bap_sema.ml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ module Std = struct
let ssa = Ssa.sub
let is_ssa = Ssa.is_transformed
let free_vars = FV.free_vars_of_sub
let compute_liveness = FV.compute_liveness
end

module Taint = Bap_sema_taint
Expand Down
119 changes: 32 additions & 87 deletions lib/bap_sema/bap_sema_free_vars.ml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ open Graphlib.Std
open Bap_ir

module Ssa = Bap_sema_ssa
module G = Bap_ir_graph
module G = Bap_tid_graph

let (++) = Set.union and (--) = Set.diff
let blk = G.Node.label
Expand All @@ -20,93 +20,38 @@ let defined_by_blk b =
| `Def def -> Set.add kill @@ Ir_def.lhs def
| `Jmp _ -> kill)

let free_vars_of_dom_tree dom root =
let rec bfs (vars,kill) root =
let cs = Tree.children dom root in
let vars = vars ++ Seq.fold cs ~init:Var.Set.empty ~f:(fun vars c ->
Ir_blk.free_vars (blk c) -- kill ++ vars) in
let kill = kill ++ defined_by_blk (blk root) in
Seq.fold cs ~init:(vars,kill) ~f:bfs in
fst @@ bfs (Var.Set.empty,Var.Set.empty) root

let dom_free_vars sub =
match Term.first blk_t sub with
| None -> Var.Set.empty
| Some entry ->
let entry = G.Node.create entry in
let cfg = G.of_sub sub in
let dom = Graphlib.dominators (module G) cfg entry in
free_vars_of_dom_tree dom entry
type blk_transfer = {
defs : Var.Set.t;
uses : Var.Set.t;
}

let blk_defs blk =
Term.enum def_t blk |>
Seq.fold ~init:Var.Set.empty ~f:(fun defs def ->
Set.add defs (Ir_def.lhs def))

let block_transitions sub =
Term.enum blk_t sub |>
Seq.fold ~init:Tid.Map.empty ~f:(fun fs blk ->
Map.add_exn fs (Term.tid blk) {
defs = blk_defs blk;
uses = Ir_blk.free_vars blk;
})

let compute_liveness sub =
let g = G.create sub in
let init = Solution.create Tid.Map.empty Var.Set.empty in
let tran = block_transitions sub in
Graphlib.fixpoint (module G) ~init ~start:G.exit ~rev:true g
~merge:Set.union
~equal:Var.Set.equal
~f:(fun n vars ->
if Tid.equal n G.exit || Tid.equal n G.start then vars
else
let {defs; uses} = Map.find_exn tran n in
vars -- defs ++ uses)

let free_vars_of_sub sub =
if Ssa.is_transformed sub
then ssa_free_vars sub
else dom_free_vars sub

let has_sub_exp x = Exp.exists (object
inherit [unit] Exp.finder
method! enter_exp exp search =
if Exp.equal exp x then search.return (Some ())
else search
end)

let substitute_exp x y = Exp.map (object
inherit Exp.mapper
method! map_exp exp =
if Exp.equal exp x then y else x
end)

let substitute blk x y =
Ir_blk.elts blk |> Seq.fold ~init:(false,blk)
~f:(fun (finished,blk) elt ->
if finished then finished,blk else match elt with
| `Phi phi ->
let exps = Ir_phi.values phi |> Seq.map ~f:snd in
if Seq.exists exps ~f:(has_sub_exp x) then
let phi = Ir_phi.map_exp phi ~f:(substitute_exp x y) in
true,Term.update phi_t blk phi
else false,blk
| `Def def ->
if has_sub_exp x (Ir_def.rhs def) then
let def = Ir_def.map_exp def ~f:(substitute_exp x y) in
true, Term.update def_t blk def
else false,blk
| `Jmp jmp ->
if Seq.exists (Ir_jmp.exps jmp) ~f:(has_sub_exp x) then
let jmp = Ir_jmp.map_exp jmp ~f:(substitute_exp x y) in
true, Term.update jmp_t blk jmp
else false,blk)

exception Finished of sub term

let finish sub blk =
Exn.raise_without_backtrace (Finished (Term.update blk_t sub blk))

let dom_bind_arg dom root sub (var,exp) =
let x = Bil.var var in
let rec bfs root =
let subst root =
let blk = Term.find_exn blk_t sub root in
let finished,blk = substitute blk x exp in
if finished then finish sub blk in
subst root;
Seq.iter (Tree.children dom root) ~f:subst;
Seq.iter (Tree.children dom root) ~f:bfs in
try bfs root; sub with Finished sub -> sub

let dom_bind_args sub entry args =
let module G = Bap_tid_graph in
let entry = Term.tid entry in
let cfg = G.create sub in
let dom = Graphlib.dominators (module G) cfg entry in
Seq.fold args ~init:sub ~f:(dom_bind_arg dom entry)

(* we do not provide algorithm that will take advantage of SSA form,
since the latter will work only for variables, but the DOM tree
algorithm will work correctly for any kind of expression. *)
let bind_args sub = match Term.first blk_t sub with
| None -> sub
| Some entry ->
Term.enum arg_t sub |>
Seq.map ~f:(fun arg -> Ir_arg.(lhs arg, rhs arg)) |>
dom_bind_args sub entry
else Solution.get (compute_liveness sub) G.start
3 changes: 2 additions & 1 deletion lib/bap_sema/bap_sema_free_vars.mli
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
open Bap_types.Std
open Graphlib.Std
open Bap_ir

val compute_liveness : sub term -> (tid, Var.Set.t) Solution.t
val free_vars_of_sub : sub term -> Var.Set.t
val bind_args : sub term -> sub term
Loading