osprey 0.4.4

Gluon bindings for Falcon
// This script finds direct branches in a binary

let array = import! "std/array.glu"
let falcon = import! "scripts/falcon.glu"
let function = import! "std/function.glu"
let io = import! "std/io.glu"
let list = import! "std/list.glu"
let types = import! "std/types.glu"

let { il, loader } = falcon
let { Expression, Operation } = falcon.types
let { List } = list
let { (|>) } = function
let {(++)} = import! "std/string.glu"
let { Option, Bool } = types

let append l m =
    match l with
    | Nil -> m
    | Cons hd tl -> Cons hd (append tl m)

let flatten l =
    match l with
    | Nil -> Nil
    | Cons hd tl -> append hd (flatten tl)

let filter f l =
    match l with
    | Nil -> Nil
    | Cons hd tl -> 
        if f hd then
            Cons hd (filter f tl)
        else
            filter f tl

let unwrap option = 
    match option with
    | Some x -> x
    | None -> error "unwrapped option with value None"

let is_some option =
    match option with
    | Some _ -> True
    | None -> False

let foldl f x xs =
    match xs with
    | Cons y ys -> foldl f (f x y) ys
    | Nil -> x

let map f l =
    match l with
    | Nil -> Nil
    | Cons hd tl -> Cons (f hd) (map f tl)


let all_constants e : IlExpression -> Bool =
    match il.expression.match_ e with
    | Scalar s -> False
    | Constant c -> True
    | Add l r -> (all_constants l) && (all_constants r)
    | Sub l r -> (all_constants l) && (all_constants r)
    | Mul l r -> (all_constants l) && (all_constants r)
    | Divu l r -> (all_constants l) && (all_constants r)
    | Modu l r -> (all_constants l) && (all_constants r)
    | Divs l r -> (all_constants l) && (all_constants r)
    | Mods l r -> (all_constants l) && (all_constants r)
    | And l r -> (all_constants l) && (all_constants r)
    | Or l r -> (all_constants l) && (all_constants r)
    | Xor l r -> (all_constants l) && (all_constants r)
    | Shl l r -> (all_constants l) && (all_constants r)
    | Shr l r -> (all_constants l) && (all_constants r)
    | Cmpeq l r -> (all_constants l) && (all_constants r)
    | Cmpneq l r -> (all_constants l) && (all_constants r)
    | Cmpltu l r -> (all_constants l) && (all_constants r)
    | Cmplts l r -> (all_constants l) && (all_constants r)
    | Zext _ src -> (all_constants src)
    | Sext _ src -> (all_constants src)
    | Trun _ src -> (all_constants src)


let filename =
    match falcon.env "FILENAME" with
    | Some filename -> filename
    | None -> error "Could not get filename"

let function_name = 
    match falcon.env "FUNCTION_NAME" with
    | Some function_name -> function_name
    | None -> "Could not get function name"


let brc_is_direct instruction : IlInstruction -> Bool =
    match il.instruction.operation instruction |> il.operation.match_ with
    | Branch target -> all_constants target
    | _ -> error "brc_is_direct given non-brc instruction"


let find_brcs function : IlFunction -> List IlInstruction =
    let is_brc i =
        match il.operation.match_ (il.instruction.operation i) with
        | Branch target -> Some i
        | _ -> None

    let control_flow_graph = il.function.control_flow_graph function
    let blocks = il.control_flow_graph.blocks control_flow_graph |> list.of
    let instructions = map il.block.instructions blocks |> map list.of |> flatten
    map is_brc instructions |> filter is_some |> map unwrap


let find_function binary function_name =
    let find functions i name =
        if i == (array.len functions) then
            None
        else
            let function_entry = array.index functions i
            match loader.function_entry.name function_entry with
            | Some entry_name ->
                if entry_name == name then
                    Some function_entry
                else
                    find functions (i + 1) name
            | None -> find functions (i + 1) name
    find (loader.loader.function_entries binary) 0 function_name


let binary =
    match loader.loader.from_file filename with
    | Some x -> x
    | None -> error "Failed to load binary"

let function_entry =
    match find_function binary function_name with
    | Some function_entry -> function_entry
    | None -> error "failed to find function entry"
    
let function = loader.function_entry.address function_entry |> loader.loader.function binary

let brcs = find_brcs function |> filter brc_is_direct
let strings = map il.instruction.str brcs

let lines = foldl (\ l r -> l ++ "\n" ++ r) "" strings

falcon.println lines