Permalink
Browse files

fixing `pshufb` insn (#726)

* reimplemented `pshufb` using a loop

* refactoring pshufb

* got rid of while loop

* refactoring

* bug fixed

* added tests

* minor

* tests reworked

* minor

* made a separate executable for x86 tests
  • Loading branch information...
gitoleg authored and ivg committed Oct 10, 2017
1 parent 6eba325 commit 698fde0902ea4c00fb0cc05049eb4543a07eed12
Showing with 245 additions and 30 deletions.
  1. +4 −1 lib_test/.merlin
  2. +2 −0 lib_test/x86/.merlin
  3. +19 −0 lib_test/x86/run_x86_tests.ml
  4. +150 −0 lib_test/x86/test_pshufb.ml
  5. +0 −1 oasis/tests
  6. +21 −0 oasis/x86
  7. +49 −28 plugins/x86/x86_lifter.ml
View
@@ -8,6 +8,8 @@ B ../_build/lib_test/bap_project
B ../_build/lib_test/bap_sema
B ../_build/lib_test/bap_trace
B ../_build/lib_test/bap_types
B ../_build/lib_test/x86
REC
S bap_core
S bap_disasm
@@ -18,5 +20,6 @@ S bap_image
S bap_project
S bap_sema
S bap_types
S bap_trace
S bap_trace
S x86
PKG oUnit
View
@@ -0,0 +1,2 @@
REC
PKG bap-x86-cpu
@@ -0,0 +1,19 @@
open Core_kernel.Std
open OUnit2
open Bap_plugins.Std
let suite = "X86" >::: [
Test_pshufb.suite;
]
let load_plugins () =
Plugins.load () |>
List.iter ~f:(function
| Ok _ -> ()
| Error (p,e)->
assert_string ("failed to load plugin from " ^ p ^ ": " ^
Error.to_string_hum e))
let () =
load_plugins ();
run_test_tt_main suite
View
@@ -0,0 +1,150 @@
(**
# rappel (don't forget -x option)
## mov to xmm0 register a 128-bit value "50 4F 4E 4D 4C 4B 4A 49 48 47 46 45 44 43 42 41"
mov rax, 0x4847464544434241
mov rbx, 0x504F4E4D4C4B4A49
pinsrq xmm0, rax, 0
pinsrq xmm0, rbx, 1
So, xmm0 will be used for original value, xmm1 - for mask, and xmm2
for our tests.
Cases:
1) mov to xmm1 register a 128 bit value with permutation indexes, just
bytes numbers as they are, i.e "0F 0E 0D 0C 0B 0A 09 08 07 06 05 04 03 02 01 00"
expected: xmm2 value should be the same as before
mov rbx, 0x0706050403020100
mov rcx, 0x0f0e0d0c0b0a0908
pinsrq xmm1, rbx, 0
pinsrq xmm1, rcx, 1
pshufb xmm2, xmm1
2) permutation mask = "0F 0E 0D 0C 0B 0A 09 08 07 06 05 04 03 01 01 01",
i.e. last three bytes of destination should be the same as byte #01.
expected: xmm2 = "50 4F 4E 4D 4C 4B 4A 49 48 47 46 45 44 42 42 42"
movaps xmm2, xmm0
mov rbx, 0x0706050403010101
mov rcx, 0x0f0e0d0c0b0a0908
pinsrq xmm1, rbx, 0
pinsrq xmm1, rcx, 1
pshufb xmm2, xmm1
3) permutation mask = "01 01 01 0C 0B 0A 09 08 07 06 05 04 03 02 01 00",
i.e. first three bytes of destination should be the same as byte #01.
expected: xmm2 = "42 42 42 4D 4C 4B 4A 49 48 47 46 45 44 43 42 41"
movaps xmm2, xmm0
mov rbx, 0x0706050403020100
mov rcx, 0x0101010c0b0a0908
pinsrq xmm1, rbx, 0
pinsrq xmm1, rcx, 1
pshufb xmm2, xmm1
4) permutation mask = "0F 0E 0D 0C 0B 0A 09 08 07 06 05 04 03 80 80 80",
i.e. last three bytes of destination shold be eqaul to zero.
expected: xmm2 = "50 4F 4E 4D 4C 4B 4A 49 48 47 46 45 44 00 00 00"
movaps xmm2, xmm0
mov rbx, 0x0706050403808080
mov rcx, 0x0f0e0d0c0b0a0908
pinsrq xmm1, rbx, 0
pinsrq xmm1, rcx, 1
pshufb xmm2, xmm1
*)
open Core_kernel.Std
open Bap.Std
open OUnit2
module Dis = Disasm_expert.Basic
open X86_env
let of_bytes s =
let str = String.filter ~f:(fun c -> Char.(c <> ' ')) s in
Word.of_string @@ sprintf "0x%s:256u" str
let (ident_mask, origin) as no_permutations =
of_bytes "0F 0E 0D 0C 0B 0A 09 08 07 06 05 04 03 02 01 00",
of_bytes "50 4F 4E 4D 4C 4B 4A 49 48 47 46 45 44 43 42 41"
let last_three_0x42 =
of_bytes "0F 0E 0D 0C 0B 0A 09 08 07 06 05 04 03 01 01 01",
of_bytes "50 4F 4E 4D 4C 4B 4A 49 48 47 46 45 44 42 42 42"
let first_three_0x42 =
of_bytes "01 01 01 0C 0B 0A 09 08 07 06 05 04 03 02 01 00",
of_bytes "42 42 42 4D 4C 4B 4A 49 48 47 46 45 44 43 42 41"
let last_three_0x00 =
of_bytes "0F 0E 0D 0C 0B 0A 09 08 07 06 05 04 03 80 80 80",
of_bytes "50 4F 4E 4D 4C 4B 4A 49 48 47 46 45 44 00 00 00"
let mem_er = Word.of_string "0x42424242:32u"
let mem_ok = Word.of_string "0x42424220:32u"
let insn_bil x =
let arch = `x86 in
let bytes = Bigstring.of_string x in
let mem = Or_error.ok_exn @@
Memory.create LittleEndian (Word.zero 32) bytes in
let mem, insn =
Or_error.ok_exn @@
Dis.with_disasm ~backend:"llvm" (Arch.to_string arch) ~f:(fun dis ->
let dis = Dis.store_asm dis |> Dis.store_kinds in
match Dis.insn_of_mem dis mem with
| Ok (mem', Some insn, `finished) -> Ok (mem', insn)
| _ -> Error (Error.of_string "invalid insn")) in
let module T = (val (target_of_arch arch)) in
Or_error.ok_exn @@ T.lift mem insn
let pshufb_rr = "\x66\x0f\x38\x00\xc1" (** pshufb %xmm0, %xmm1 *)
let pshufb_rm = "\x66\x0f\x38\x00\x00" (** pshufb %xmm0, (%eax) *)
(** tests that permutations works as expected *)
let test_rr (mask, expected) ctxt =
let xmm0 = X86_env.ymms.(0) in
let xmm1 = X86_env.ymms.(1) in
let bil = Bil.[
move xmm0 (int origin);
move xmm1 (int mask);
] @ insn_bil pshufb_rr in
let c = Stmt.eval bil (new Bili.context) in
assert_bool "pshufb: got wrong result" @@
match c#lookup xmm0 with
| None -> false
| Some r ->
match Bil.Result.value r with
| Bil.Imm word -> Word.equal word expected
| _ -> false
class exn_visitor = object
inherit [bool] Stmt.visitor
method! enter_cpuexn _ _ = true
end
(** tests that memory alignment check is performed, i.e. cpuexn could be
raise in some cases raised *)
let test_rm addr expected ctxt =
let open X86_env.R32 in
let xmm0 = X86_env.ymms.(0) in
let bil = Bil.[
move rax (int addr);
move mem
(store ~mem:(var mem) ~addr:(int addr) (int ident_mask) LittleEndian `r256);
move xmm0 (int origin);
] @ insn_bil pshufb_rm in
assert_bool "pshufb: memory alignment" @@
(new exn_visitor)#run bil false
let suite = "pshufb" >::: [
"no permuatations" >:: test_rr no_permutations;
"last three bytes = 0x42" >:: test_rr last_three_0x42;
"first three bytes = 0x42" >:: test_rr first_three_0x42;
"last three bytes = 0x0" >:: test_rr last_three_0x00;
"memory alignment is ok" >:: test_rm mem_ok false;
"memory alignment is error" >:: test_rm mem_er true;
]
View
@@ -44,7 +44,6 @@ Library sema_test
Install: false
Modules: Test_ir
Executable run_tests
Path: lib_test/bap
MainIs: run_tests.ml
View
@@ -52,3 +52,24 @@ Library x86_plugin
X86_tools_vector,
X86_utils
XMETAExtraLines: tags="disassembler, lifter, x86, abi"
Library x86_test
Path: lib_test/x86
Build$: flag(tests) && (flag(everything) || flag(x86))
CompiledObject: best
BuildDepends: bap, oUnit, bap-x86-cpu
Install: false
Modules: Test_pshufb
Executable run_x86_tests
Path: lib_test/x86
Build$: flag(tests) && (flag(everything) || flag(x86))
CompiledObject: best
BuildDepends: bap.plugins, findlib.dynload, oUnit, x86_test
Install: false
MainIs: run_x86_tests.ml
Test x86_tests
TestTools: run_x86_tests
Run$: flag(tests) && (flag(everything) || flag(x86))
Command: $run_x86_tests
View
@@ -772,34 +772,55 @@ module ToIR = struct
(match vsrc with
| None -> [assn t dst dwords]
| Some vdst -> [assn t vdst dwords])
| Pshufb (t, dst, src, vsrc) ->
let order_e = op2e t src in
let dst_e = op2e t dst in
let get_bit i =
let highbit = Bil.Extract (((i*8)+7), ((i*8)+7), order_e) in
(* this part of the code previously also used Typecheck.infer_ast
* (indirectly, by calling Ast_convenience.extract_byte_symbolic with
* index as the last argument).
* I've read the relevant parts of infer_ast and extract_byte_symbolic
* and also the helpful comments below "3 bits" and "4 bits"
* and it seems those are the appropriate widths we get out of
* infer_ast, so I'm passing those in directly now.
* Imo, this is a lot less dubious than the thingy above. *)
let (index, index_width) = match t with
| Type.Imm 64 -> (Bil.Extract (((i*8)+2), ((i*8)+0), order_e), 64) (* 3 bits *)
| Type.Imm 128 -> (Bil.Extract (((i*8)+3), ((i*8)+0), order_e), 128) (* 4 bits *)
| Type.Imm 256 -> (Bil.Extract (((i*8)+3), ((i*8)+0), order_e), 256) (* 4 bits *)
| _ -> disfailwith "invalid size for pshufb"
in
let index = Bil.(Cast (UNSIGNED, !!t, index)) in
let atindex = extract_byte_symbolic_with_width dst_e index index_width in
Bil.Ite (highbit, int_exp 0 8, atindex)
in
let n = !!t / 8 in
let e = concat_explist (List.map ~f:get_bit (List.range ~stride:(-1) ~start:`exclusive ~stop:`inclusive n 0)) in
(match vsrc with
| None -> [assn t dst e]
| Some vdst -> [assn t vdst e])
| Pshufb (exp_type, dst_op, src_op, vsrc) ->
let op_size = bitwidth_of_type exp_type in
let index_bits = match op_size with
| 64 -> 3
| 128 | 256 -> 4
| _ -> disfailwith "invalid size for pshufb" in
let foreach_byte f = List.concat @@ List.init (op_size / 8) ~f in
let src = op2e exp_type src_op in
let dst = op2e exp_type dst_op in
let dst_op = Option.value ~default:dst_op vsrc in
let zero = Bil.int (Word.zero op_size) in
let msb_one = Bil.int (Word.of_int ~width:8 0x80) in
let byte_t = Type.imm 8 in
let byte = int_exp 8 8 in
let iv = tmp ~name:"i" byte_t in
let mask_byte_i = tmp byte_t in
let tmp_byte = tmp exp_type in
let tmp_dst = tmp exp_type in
let ind = tmp byte_t in
let check_mem_alignment = match src_op with
| Oaddr addr when (op_size = 128) ->
let word_size = width_of_mode mode in
let zero = Bil.int (Word.zero word_size) in
let oxf = Bil.int (Word.of_int ~width:word_size 0xf) in
[ Bil.(if_ (oxf land addr <> zero) [cpuexn 13] []) ]
| _ -> [] in
List.concat [
check_mem_alignment;
[Bil.move tmp_dst zero];
foreach_byte (fun i ->
Bil.[
iv := int (Word.of_int ~width:8 i);
mask_byte_i := extract 7 0 (src lsr (var iv * byte));
if_ (msb_one land var mask_byte_i = msb_one) [
tmp_byte := zero;
] (* else *) [
ind := cast unsigned 8 (extract index_bits 0 (var mask_byte_i));
tmp_byte :=
cast unsigned op_size (extract 7 0 (dst lsr (var ind * byte)))
];
tmp_dst := Bil.(var tmp_dst lor (var tmp_byte lsl (var iv * byte)));
]);
[assn exp_type dst_op (Bil.var tmp_dst)]
]
| Lea(t, r, a) when pref = [] ->
(* See Table 3-64 *)
(* previously, it checked whether addrbits > opbits before the cast_low.

0 comments on commit 698fde0

Please sign in to comment.