Compare commits

...

2 commits

Author SHA1 Message Date
Théophile Bastian 344ac84ef3 Ignore ghost subroutines for clang
A ghost subroutine is a subroutine having, in the IR representation, no
content. At clang -O0, some might be generated, eg.

<foo_func>:
foo_addr        nop

which translates to Empty in BIL.
2019-04-05 11:23:18 +02:00
Théophile Bastian 6c18d9f537 Use rbp only on the subs where we need it 2019-04-05 11:23:13 +02:00

View file

@ -98,6 +98,15 @@ let opt_addr_of_blk_elt = function
| `Jmp jmp -> opt_addr_of jmp
| `Phi phi -> opt_addr_of phi
let is_ghost_sub sub =
(** Check whether the subroutine has content *)
let is_ghost_block blk =
BStd.Blk.elts blk
|> BStd.Seq.is_empty
in
let blk_seq = BStd.Term.enum BStd.blk_t sub in
BStd.Seq.for_all blk_seq ~f:is_ghost_block
let entrypoint_address blk =
(** Find the first instruction address in the current block.
Return None if no instruction has address. *)
@ -344,13 +353,14 @@ let is_single_free_reg expr =
)
let process_def (local_state: block_local_state) def (cur_reg: reg_pos)
allow_rbp
: (reg_pos option * block_local_state) =
let lose_track = Some CfaLostTrack in
let cur_cfa, cur_rbp = cur_reg in
let out_cfa =
(match cur_cfa, Regs.X86_64.of_var (BStd.Def.lhs def) with
| RspOffset(cur_offset), Some reg when reg = Regs.X86_64.rsp ->
(match cur_cfa, Regs.X86_64.of_var (BStd.Def.lhs def), allow_rbp with
| RspOffset(cur_offset), Some reg, _ when reg = Regs.X86_64.rsp ->
let exp = BStd.Def.rhs def in
(match is_single_free_reg exp with
| Some (bil_var, dw_var) when dw_var = Regs.X86_64.rsp ->
@ -362,7 +372,7 @@ let process_def (local_state: block_local_state) def (cur_reg: reg_pos)
)
| _ -> lose_track
)
| RspOffset(cur_offset), Some reg when reg = Regs.X86_64.rbp ->
| RspOffset(cur_offset), Some reg, true when reg = Regs.X86_64.rbp ->
(* We have CFA=rsp+k and a line %rbp <- [expr].
Might be a %rbp <- %rsp *)
let exp = BStd.Def.rhs def in
@ -383,7 +393,7 @@ let process_def (local_state: block_local_state) def (cur_reg: reg_pos)
)
| _ -> None
)
| RbpOffset(cur_offset), Some reg when reg = Regs.X86_64.rbp ->
| RbpOffset(cur_offset), Some reg, true when reg = Regs.X86_64.rbp ->
(* Assume we are overwriting %rbp with something — we must revert to
some rsp-based indexing *)
(* FIXME don't assume the rsp offset will always be 8, find a smart way
@ -392,6 +402,7 @@ let process_def (local_state: block_local_state) def (cur_reg: reg_pos)
value is read from the stack.
*)
Some (RspOffset(Int64.of_int 16))
| RbpOffset _, _, false -> assert false
| _ -> None
) in
@ -518,7 +529,7 @@ let process_jmp jmp (cur_reg: reg_pos)
| _ -> None
let process_blk
next_instr_graph rbp_pop_set (block_init: reg_pos) blk
next_instr_graph rbp_pop_set allow_rbp (block_init: reg_pos) blk
: (reg_changes_fde * reg_pos) =
(** Extracts the registers (CFA+RBP) changes of a block. *)
@ -539,7 +550,8 @@ let process_blk
let fold_elt (accu, cur_reg, cur_local_state) elt = match elt with
| `Def(def) ->
let new_offset, new_state = process_def cur_local_state def cur_reg in
let new_offset, new_state =
process_def cur_local_state def cur_reg allow_rbp in
apply_offset
(opt_addr_of def) (accu, cur_reg, new_state) new_offset
| `Jmp(jmp) ->
@ -655,6 +667,7 @@ let process_sub sub next_instr_graph : subroutine_cfa_data =
let rbp_pop_set = find_rbp_pop_set cfg entry_blk in
let rec dfs_process
allow_rbp
(sub_changes: (reg_changes_fde * reg_pos) TIdMap.t)
node
(entry_offset: reg_pos) =
@ -667,26 +680,56 @@ let process_sub sub next_instr_graph : subroutine_cfa_data =
| None ->
(* Not yet visited: compute the changes *)
let cur_blk_changes, end_reg =
process_blk next_instr_graph rbp_pop_set entry_offset cur_blk in
process_blk next_instr_graph rbp_pop_set
allow_rbp entry_offset cur_blk in
let n_sub_changes =
TIdMap.add tid (cur_blk_changes, entry_offset) sub_changes in
BStd.Seq.fold (CFG.Node.succs node cfg)
~f:(fun accu child -> dfs_process accu child end_reg)
~f:(fun accu child -> dfs_process allow_rbp accu child end_reg)
~init:n_sub_changes
| Some (_, former_entry_offset) ->
(* Already visited: check that entry values are matching *)
if entry_offset <> former_entry_offset then
( Format.eprintf "Found inconsistency (0x%Lx): %a -- %a@."
if entry_offset <> former_entry_offset then (
if allow_rbp then
Format.eprintf "Found inconsistency (0x%Lx): %a -- %a@."
(int64_addr_of cur_blk)
pp_reg_pos entry_offset pp_reg_pos former_entry_offset
;
raise (Inconsistent tid) )
pp_reg_pos entry_offset pp_reg_pos former_entry_offset ;
raise (Inconsistent tid)
)
else
sub_changes
in
let with_rbp_if_needed initial_offset =
(* Tries first without allowing CFA=rbp+k, then allowing it if the first
result was either inconsistent or lost track *)
let not_losing_track synth_result =
let lost_track = TIdMap.exists
(fun _ (_, (cfa_pos, _)) -> match cfa_pos with
| CfaLostTrack -> true
| _ -> false) synth_result
in
(match lost_track with
| true -> None
| false -> Some synth_result)
in
let without_rbp =
(try
dfs_process false TIdMap.empty entry_blk initial_offset
|> not_losing_track
with Inconsistent _ -> None
)
in
(match without_rbp with
| Some correct_res -> correct_res
| None ->
dfs_process true TIdMap.empty entry_blk initial_offset)
in
let initial_offset = (RspOffset initial_cfa_rsp_offset, RbpUndef) in
let changes_map = dfs_process TIdMap.empty entry_blk initial_offset in
(* Try first without rbp, then with rbp upon failure *)
let changes_map = with_rbp_if_needed initial_offset in
let merged_changes = TIdMap.fold
(fun _ (cfa_changes, _) accu -> AddrMap.union (fun _ v1 v2 ->
@ -712,8 +755,12 @@ let of_prog prog next_instr_graph : subroutine_cfa_map =
(** Extracts the `cfa_changes` of a program *)
let fold_step accu sub =
(try
let subroutine_data = process_sub sub next_instr_graph in
StrMap.add (BStd.Sub.name sub) subroutine_data accu
(match is_ghost_sub sub with
| true -> accu
| false ->
let subroutine_data = process_sub sub next_instr_graph in
StrMap.add (BStd.Sub.name sub) subroutine_data accu
)
with
| InvalidSub -> accu
| Inconsistent tid ->
@ -733,9 +780,13 @@ let build_sub_ranges prog: (memory_address) AddrMap.t =
easy fast access to a member (cf Map.S.find_first) *)
let fold_subroutine accu sub =
let first_addr = int64_addr_of sub in
let last_addr = find_last_addr sub in
AddrMap.add first_addr (last_addr) accu
(match is_ghost_sub sub with
| true -> accu
| false ->
let first_addr = int64_addr_of sub in
let last_addr = find_last_addr sub in
AddrMap.add first_addr (last_addr) accu
)
in
let subroutines = BStd.Term.enum BStd.sub_t prog in