github.com/decomp/exp@v0.0.0-20210624183419-6d058f5e1da6/lift/x86/lift_test.go (about) 1 package x86 2 3 import ( 4 "io/ioutil" 5 "log" 6 "os" 7 "path/filepath" 8 "testing" 9 10 "github.com/decomp/exp/bin" 11 _ "github.com/decomp/exp/bin/elf" // register ELF decoder 12 _ "github.com/decomp/exp/bin/pe" // register PE decoder 13 _ "github.com/decomp/exp/bin/pef" // register PEF decoder 14 "github.com/decomp/exp/bin/raw" 15 "github.com/llir/llvm/ir" 16 "github.com/mewkiz/pkg/diffutil" 17 "github.com/pkg/errors" 18 ) 19 20 func TestLift(t *testing.T) { 21 golden := []struct { 22 // Base directory; which may contain decomp JSON files. 23 dir string 24 // Path to input binary executable or object file. 25 in string 26 // Path to output LLVM IR assembly file. 27 out string 28 // Raw machine architecture; or 0 if any format other than raw. 29 arch bin.Arch 30 }{ 31 // File formats. 32 // 33 // * .bin - raw executable files 34 // * .o - ELF object files 35 // * .so - ELF shared object files 36 // * .out - ELF executable files 37 // * .coff - COFF object files 38 {dir: "testdata/x86_32/format", in: "format.bin", out: "format_bin.ll", arch: bin.ArchX86_32}, 39 {dir: "testdata/x86_32/format", in: "format_elf.o", out: "format_o.ll"}, 40 {dir: "testdata/x86_32/format", in: "format_elf.so", out: "format_so.ll"}, 41 {dir: "testdata/x86_32/format", in: "format_elf.out", out: "format_out.ll"}, 42 {dir: "testdata/x86_64/format", in: "format.bin", out: "format_bin.ll", arch: bin.ArchX86_32}, 43 {dir: "testdata/x86_64/format", in: "format_elf.o", out: "format_o.ll"}, 44 {dir: "testdata/x86_64/format", in: "format_elf.so", out: "format_so.ll"}, 45 {dir: "testdata/x86_64/format", in: "format_elf.out", out: "format_out.ll"}, 46 // TODO: Add support for COFF files. 47 //{in: "testdata/format.coff", out: "testdata/format_coff.ll"}, 48 49 // Arithmetic instructions. 50 {dir: "testdata/x86_32/arithmetic", in: "arithmetic.so", out: "arithmetic.ll"}, 51 {dir: "testdata/x86_64/arithmetic", in: "arithmetic.so", out: "arithmetic.ll"}, 52 53 // Import functions from dynamic libraries. 54 {dir: "testdata/x86_32/import", in: "import.out", out: "import.ll"}, 55 {dir: "testdata/x86_64/import", in: "import.out", out: "import.ll"}, 56 57 // === [ FPU instructions ] ============================================== 58 // 59 // --- [ x87 FPU Data Transfer Instructions ] ---------------------------- 60 // 61 // * FBLD 62 // * FBSTP 63 // * FILD 64 {dir: "testdata/x86_32/fpu/fild", in: "fild.so", out: "fild.ll"}, 65 {dir: "testdata/x86_64/fpu/fild", in: "fild.so", out: "fild.ll"}, 66 // * FIST 67 // * FISTP 68 // * FLD 69 {dir: "testdata/x86_32/fpu/fld", in: "fld.so", out: "fld.ll"}, 70 {dir: "testdata/x86_64/fpu/fld", in: "fld.so", out: "fld.ll"}, 71 // * FST 72 // * FSTP 73 // * FXCH 74 // 75 // ___ [ FCMOVcc - Floating-Point Conditional Move Instructions ] ________ 76 // 77 // * FCMOVB 78 // * FCMOVBE 79 // * FCMOVE 80 // * FCMOVNB 81 // * FCMOVNBE 82 // * FCMOVNE 83 // * FCMOVNU 84 // * FCMOVU 85 // 86 // --- [ x87 FPU Load Constants Instructions ] --------------------------- 87 // 88 // * FLD1 89 {dir: "testdata/x86_32/fpu/fld1", in: "fld1.so", out: "fld1.ll"}, 90 {dir: "testdata/x86_64/fpu/fld1", in: "fld1.so", out: "fld1.ll"}, 91 // * FLDL2E 92 {dir: "testdata/x86_32/fpu/fldl2e", in: "fldl2e.so", out: "fldl2e.ll"}, 93 {dir: "testdata/x86_64/fpu/fldl2e", in: "fldl2e.so", out: "fldl2e.ll"}, 94 // * FLDL2T 95 {dir: "testdata/x86_32/fpu/fldl2t", in: "fldl2t.so", out: "fldl2t.ll"}, 96 {dir: "testdata/x86_64/fpu/fldl2t", in: "fldl2t.so", out: "fldl2t.ll"}, 97 // * FLDLG2 98 {dir: "testdata/x86_32/fpu/fldlg2", in: "fldlg2.so", out: "fldlg2.ll"}, 99 {dir: "testdata/x86_64/fpu/fldlg2", in: "fldlg2.so", out: "fldlg2.ll"}, 100 // * FLDLN2 101 {dir: "testdata/x86_32/fpu/fldln2", in: "fldln2.so", out: "fldln2.ll"}, 102 {dir: "testdata/x86_64/fpu/fldln2", in: "fldln2.so", out: "fldln2.ll"}, 103 // * FLDPI 104 {dir: "testdata/x86_32/fpu/fldpi", in: "fldpi.so", out: "fldpi.ll"}, 105 {dir: "testdata/x86_64/fpu/fldpi", in: "fldpi.so", out: "fldpi.ll"}, 106 // * FLDZ 107 {dir: "testdata/x86_32/fpu/fldz", in: "fldz.so", out: "fldz.ll"}, 108 {dir: "testdata/x86_64/fpu/fldz", in: "fldz.so", out: "fldz.ll"}, 109 } 110 wd, err := os.Getwd() 111 if err != nil { 112 t.Fatalf("unable to retrieve current working directory; %+v", err) 113 } 114 for _, g := range golden { 115 in := filepath.Join(g.dir, g.in) 116 log.Printf("testing: %q", in) 117 if err := os.Chdir(wd); err != nil { 118 t.Errorf("%q: unable to change working directory; %+v", in, err) 119 continue 120 } 121 if err := os.Chdir(g.dir); err != nil { 122 t.Errorf("%q: unable to change working directory; %+v", in, err) 123 continue 124 } 125 l, err := newLifter(g.in, g.arch) 126 if err != nil { 127 t.Errorf("%q: unable to prepare lifter; %+v", in, err) 128 continue 129 } 130 131 // Create function lifters. 132 for _, funcAddr := range l.FuncAddrs { 133 asmFunc, err := l.DecodeFunc(funcAddr) 134 if err != nil { 135 t.Errorf("%q: unable to decode function; %+v", in, err) 136 continue 137 } 138 f := l.NewFunc(asmFunc) 139 l.Funcs[funcAddr] = f 140 } 141 142 // Lift functions. 143 module := &ir.Module{} 144 for _, funcAddr := range l.FuncAddrs { 145 f, ok := l.Funcs[funcAddr] 146 if !ok { 147 continue 148 } 149 f.Lift() 150 module.Funcs = append(module.Funcs, f.Func) 151 } 152 buf, err := ioutil.ReadFile(g.out) 153 if err != nil { 154 t.Errorf("%q: unable to read file: %+v", in, err) 155 continue 156 } 157 got := module.String() 158 want := string(buf) 159 if got != want { 160 diffutil.Diff(want, got, false, g.in) 161 t.Errorf("%q: module mismatch; expected `%v`, got `%v`", in, want, got) 162 continue 163 } 164 } 165 } 166 167 // newLifter returns a new x86 to LLVM IR lifter for the given binary 168 // executable. 169 func newLifter(path string, arch bin.Arch) (*Lifter, error) { 170 if arch != 0 { 171 file, err := raw.ParseFile(path, arch) 172 if err != nil { 173 return nil, errors.WithStack(err) 174 } 175 return NewLifter(file) 176 } 177 file, err := bin.ParseFile(path) 178 if err != nil { 179 return nil, errors.WithStack(err) 180 } 181 return NewLifter(file) 182 }